Skip to content

Commit

Permalink
Store RuneIds in edicts as two values
Browse files Browse the repository at this point in the history
  • Loading branch information
casey committed Mar 22, 2024
1 parent 8ddbe9f commit 3b25ac9
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 204 deletions.
12 changes: 3 additions & 9 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,12 +909,8 @@ impl Index {
let mut balances = Vec::new();
let mut i = 0;
while i < balances_buffer.len() {
let (id, length) = varint::decode(&balances_buffer[i..]).unwrap();
let ((id, amount), length) = RuneId::decode_balance(&balances_buffer[i..]).unwrap();
i += length;
let (amount, length) = varint::decode(&balances_buffer[i..]).unwrap();
i += length;

let id = RuneId::try_from(id).unwrap();

let entry = RuneEntry::load(id_to_rune_entries.get(id.store())?.unwrap().value());

Expand Down Expand Up @@ -1002,11 +998,9 @@ impl Index {
let mut balances = Vec::new();
let mut i = 0;
while i < balances_buffer.len() {
let (id, length) = varint::decode(&balances_buffer[i..]).unwrap();
i += length;
let (balance, length) = varint::decode(&balances_buffer[i..]).unwrap();
let ((id, balance), length) = RuneId::decode_balance(&balances_buffer[i..]).unwrap();
i += length;
balances.push((RuneId::try_from(id)?, balance));
balances.push((id, balance));
}

result.push((outpoint, balances));
Expand Down
2 changes: 1 addition & 1 deletion src/index/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ impl Entry for RuneEntry {
}
}

pub(super) type RuneIdValue = (u32, u16);
pub(super) type RuneIdValue = (u32, u32);

impl Entry for RuneId {
type Value = RuneIdValue;
Expand Down
2 changes: 1 addition & 1 deletion src/index/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ impl<'index> Updater<'index> {
};

for (i, (tx, txid)) in block.txdata.iter().enumerate() {
rune_updater.index_runes(i, tx, *txid)?;
rune_updater.index_runes(u32::try_from(i).unwrap(), tx, *txid)?;
}

for (rune_id, update) in rune_updater.updates {
Expand Down
29 changes: 7 additions & 22 deletions src/index/updater/rune_updater.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
super::*,
crate::runes::{varint, Edict, Runestone},
crate::runes::{Edict, Runestone},
};

struct Claim {
Expand Down Expand Up @@ -41,12 +41,7 @@ pub(super) struct RuneUpdater<'a, 'db, 'tx> {
}

impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
pub(super) fn index_runes(
&mut self,
tx_index: usize,
tx: &Transaction,
txid: Txid,
) -> Result<()> {
pub(super) fn index_runes(&mut self, tx_index: u32, tx: &Transaction, txid: Txid) -> Result<()> {
let runestone = Runestone::from_transaction(tx);

let mut unallocated = self.unallocated(tx)?;
Expand Down Expand Up @@ -76,14 +71,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
update.supply += claim.limit;
}

// Nota bene: Because it would require constructing a block
// with 2**16 + 1 transactions, there is no test that checks that
// an eching in a transaction with an out-of-bounds index is
// ignored.
let mut etched = u16::try_from(tx_index)
.ok()
.and_then(|tx_index| self.etched(tx_index, tx, &runestone).transpose())
.transpose()?;
let mut etched = self.etched(tx_index, tx, &runestone)?;

if !cenotaph {
for Edict { id, amount, output } in runestone.edicts {
Expand Down Expand Up @@ -221,8 +209,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
balances.sort();

for (id, balance) in balances {
varint::encode_to_vec(id.into(), &mut buffer);
varint::encode_to_vec(balance, &mut buffer);
id.encode_balance(balance, &mut buffer);
}

self.outpoint_to_balances.insert(
Expand Down Expand Up @@ -328,7 +315,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {

fn etched(
&mut self,
tx_index: u16,
tx_index: u32,
tx: &Transaction,
runestone: &Runestone,
) -> Result<Option<Etched>> {
Expand Down Expand Up @@ -446,11 +433,9 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
let buffer = guard.value();
let mut i = 0;
while i < buffer.len() {
let (id, len) = varint::decode(&buffer[i..]).unwrap();
i += len;
let (balance, len) = varint::decode(&buffer[i..]).unwrap();
let ((id, balance), len) = RuneId::decode_balance(&buffer[i..]).unwrap();
i += len;
*unallocated.entry(id.try_into().unwrap()).or_default() += balance;
*unallocated.entry(id).or_default() += balance;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use {
},
into_usize::IntoUsize,
representation::Representation,
runes::{varint, Etching},
runes::Etching,
settings::Settings,
subcommand::{Subcommand, SubcommandResult},
tally::Tally,
Expand Down
4 changes: 1 addition & 3 deletions src/runes/edict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ pub struct Edict {
impl Edict {
pub(crate) fn from_integers(
tx: &Transaction,
id: u128,
id: RuneId,
amount: u128,
output: u128,
) -> Option<Self> {
let id = RuneId::try_from(id).ok()?;

let Ok(output) = u32::try_from(output) else {
return None;
};
Expand Down
107 changes: 78 additions & 29 deletions src/runes/rune_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,58 @@ use super::*;
#[derive(Debug, PartialEq, Copy, Clone, Hash, Eq, Ord, PartialOrd, Default)]
pub struct RuneId {
pub block: u32,
pub tx: u16,
pub tx: u32,
}

impl TryFrom<u128> for RuneId {
type Error = Error;
impl RuneId {
pub(crate) fn delta(self, next: RuneId) -> Option<(u128, u128)> {
let block = next.block.checked_sub(self.block)?;

fn try_from(n: u128) -> Result<Self, Error> {
let id = Self {
block: u32::try_from(n >> 16)?,
tx: u16::try_from(n & 0xFFFF).unwrap(),
let tx = if block == 0 {
next.tx.checked_sub(self.tx)?
} else {
next.tx
};

Some((block.into(), tx.into()))
}

pub(crate) fn next(self: RuneId, block: u128, tx: u128) -> Option<RuneId> {
let id = RuneId {
block: self.block.checked_add(block.try_into().ok()?)?,
tx: if block == 0 {
self.tx.checked_add(tx.try_into().ok()?)?
} else {
tx.try_into().ok()?
},
};

if id.block == 0 && id.tx > 0 {
bail!("invalid rune ID: {id}")
return None;
}

Ok(id)
Some(id)
}
}

impl From<RuneId> for u128 {
fn from(id: RuneId) -> Self {
u128::from(id.block) << 16 | u128::from(id.tx)
pub(crate) fn encode_balance(self, balance: u128, buffer: &mut Vec<u8>) {
varint::encode_to_vec(self.block.into(), buffer);
varint::encode_to_vec(self.tx.into(), buffer);
varint::encode_to_vec(balance, buffer);
}

pub(crate) fn decode_balance(buffer: &[u8]) -> Option<((RuneId, u128), usize)> {
let mut len = 0;
let (block, block_len) = varint::decode(&buffer[len..])?;
len += block_len;
let (tx, tx_len) = varint::decode(&buffer[len..])?;
len += tx_len;
let id = RuneId {
block: block.try_into().ok()?,
tx: tx.try_into().ok()?,
};
let (balance, balance_len) = varint::decode(&buffer[len..])?;
len += balance_len;
Some(((id, balance), len))
}
}

Expand Down Expand Up @@ -73,11 +102,44 @@ mod tests {
use super::*;

#[test]
fn rune_id_to_128() {
fn delta() {
let mut expected = [
RuneId { block: 1, tx: 2 },
RuneId { block: 1, tx: 1 },
RuneId { block: 3, tx: 1 },
RuneId { block: 2, tx: 0 },
];

expected.sort();

assert_eq!(
0b11_0000_0000_0000_0001u128,
RuneId { block: 3, tx: 1 }.into()
expected,
[
RuneId { block: 1, tx: 1 },
RuneId { block: 1, tx: 2 },
RuneId { block: 2, tx: 0 },
RuneId { block: 3, tx: 1 },
]
);

let mut previous = RuneId::default();
let mut deltas = Vec::new();
for id in expected {
deltas.push(previous.delta(id).unwrap());
previous = id;
}

assert_eq!(deltas, [(1, 1), (0, 1), (1, 0), (1, 1)]);

let mut previous = RuneId::default();
let mut actual = Vec::new();
for (block, tx) in deltas {
let next = previous.next(block, tx).unwrap();
actual.push(next);
previous = next;
}

assert_eq!(actual, expected);
}

#[test]
Expand All @@ -95,19 +157,6 @@ mod tests {
assert_eq!("1:2".parse::<RuneId>().unwrap(), RuneId { block: 1, tx: 2 });
}

#[test]
fn try_from() {
assert_eq!(
RuneId::try_from(0x060504030201).unwrap(),
RuneId {
block: 0x06050403,
tx: 0x0201
}
);

assert!(RuneId::try_from(0x07060504030201).is_err());
}

#[test]
fn serde() {
let rune_id = RuneId { block: 1, tx: 2 };
Expand Down
Loading

0 comments on commit 3b25ac9

Please sign in to comment.