diff --git a/src/index.rs b/src/index.rs index fa14f2637e..2689380b33 100644 --- a/src/index.rs +++ b/src/index.rs @@ -30,7 +30,7 @@ use { }, }; -pub use self::entry::RuneEntry; +pub use {self::entry::RuneEntry, entry::MintEntry}; pub(crate) mod entry; mod fetcher; @@ -41,7 +41,7 @@ mod updater; #[cfg(test)] pub(crate) mod testing; -const SCHEMA_VERSION: u64 = 16; +const SCHEMA_VERSION: u64 = 17; macro_rules! define_table { ($name:ident, $key:ty, $value:ty) => { diff --git a/src/index/entry.rs b/src/index/entry.rs index 116ee448ea..11f7e8c829 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -31,11 +31,9 @@ impl Entry for Header { #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] pub struct RuneEntry { pub burned: u128, - pub deadline: Option, pub divisibility: u8, - pub end: Option, pub etching: Txid, - pub limit: Option, + pub mint: Option, pub mints: u64, pub number: u64, pub rune: Rune, @@ -46,21 +44,30 @@ pub struct RuneEntry { } pub(super) type RuneEntryValue = ( - u128, // burned + u128, // burned + u8, // divisibility + (u128, u128), // etching + Option, // mint parameters + u64, // mints + u64, // number + u128, // rune + u32, // spacers + u128, // supply + Option, // symbol + u32, // timestamp +); + +#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize, Default)] +pub struct MintEntry { + pub deadline: Option, + pub end: Option, + pub limit: Option, +} + +type MintEntryValue = ( Option, // deadline - u8, // divisibility Option, // end - (u128, u128), // etching Option, // limit - ( - u64, // mints - u64, // number - ), - u128, // rune - u32, // spacers - u128, // supply - Option, // symbol - u32, // timestamp ); impl RuneEntry { @@ -76,11 +83,9 @@ impl Default for RuneEntry { fn default() -> Self { Self { burned: 0, - deadline: None, divisibility: 0, - end: None, etching: Txid::all_zeros(), - limit: None, + mint: None, mints: 0, number: 0, rune: Rune(0), @@ -98,12 +103,11 @@ impl Entry for RuneEntry { fn load( ( burned, - deadline, divisibility, - end, etching, - limit, - (mints, number), + mint, + mints, + number, rune, spacers, supply, @@ -113,9 +117,7 @@ impl Entry for RuneEntry { ) -> Self { Self { burned, - deadline, divisibility, - end, etching: { let low = etching.0.to_le_bytes(); let high = etching.1.to_le_bytes(); @@ -126,7 +128,11 @@ impl Entry for RuneEntry { high[14], high[15], ]) }, - limit, + mint: mint.map(|(deadline, end, limit)| MintEntry { + deadline, + end, + limit, + }), mints, number, rune: Rune(rune), @@ -140,9 +146,7 @@ impl Entry for RuneEntry { fn store(self) -> Self::Value { ( self.burned, - self.deadline, self.divisibility, - self.end, { let bytes = self.etching.to_byte_array(); ( @@ -156,8 +160,15 @@ impl Entry for RuneEntry { ]), ) }, - self.limit, - (self.mints, self.number), + self.mint.map( + |MintEntry { + deadline, + end, + limit, + }| (deadline, end, limit), + ), + self.mints, + self.number, self.rune.0, self.spacers, self.supply, @@ -434,15 +445,17 @@ mod tests { fn rune_entry() { let entry = RuneEntry { burned: 1, - deadline: Some(2), divisibility: 3, - end: Some(4), etching: Txid::from_byte_array([ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, ]), - limit: Some(5), + mint: Some(MintEntry { + deadline: Some(2), + end: Some(4), + limit: Some(5), + }), mints: 11, number: 6, rune: Rune(7), @@ -454,15 +467,14 @@ mod tests { let value = ( 1, - Some(2), 3, - Some(4), ( 0x0F0E0D0C0B0A09080706050403020100, 0x1F1E1D1C1B1A19181716151413121110, ), - Some(5), - (11, 6), + Some((Some(2), Some(4), Some(5))), + 11, + 6, 7, 8, 9, diff --git a/src/index/testing.rs b/src/index/testing.rs index bb705861a5..e7774aabae 100644 --- a/src/index/testing.rs +++ b/src/index/testing.rs @@ -120,9 +120,9 @@ impl Context { balances.sort_by_key(|(id, _)| *id); } - assert_eq!(runes, self.index.runes().unwrap()); + pretty_assert_eq!(runes, self.index.runes().unwrap()); - assert_eq!(balances, self.index.get_rune_balances().unwrap()); + pretty_assert_eq!(balances, self.index.get_rune_balances().unwrap()); let mut outstanding: HashMap = HashMap::new(); @@ -133,7 +133,7 @@ impl Context { } for (id, entry) in runes { - assert_eq!( + pretty_assert_eq!( outstanding.get(id).copied().unwrap_or_default(), entry.supply - entry.burned ); diff --git a/src/index/updater/rune_updater.rs b/src/index/updater/rune_updater.rs index c3ec79e410..651c73e87f 100644 --- a/src/index/updater/rune_updater.rs +++ b/src/index/updater/rune_updater.rs @@ -9,11 +9,9 @@ fn claim(id: u128) -> Option { struct Allocation { balance: u128, - deadline: Option, divisibility: u8, - end: Option, id: u128, - limit: Option, + mint: Option, rune: Rune, spacers: u32, symbol: Option, @@ -112,11 +110,6 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> { Rune::reserved(reserved_runes.into()) }; - let (limit, term) = match (etching.limit, etching.term) { - (None, Some(term)) => (Some(runes::MAX_LIMIT), Some(term)), - (limit, term) => (limit, term), - }; - // Construct an allocation, representing the new runes that may be // allocated. Beware: Because it would require constructing a block // with 2**16 + 1 transactions, there is no test that checks that @@ -124,23 +117,25 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> { // ignored. match u16::try_from(index) { Ok(index) => Some(Allocation { - balance: if let Some(limit) = limit { - if term == Some(0) { + balance: if let Some(mint) = etching.mint { + if mint.term == Some(0) { 0 } else { - limit + mint.limit.unwrap_or(runes::MAX_LIMIT) } } else { u128::max_value() }, - deadline: etching.deadline, divisibility: etching.divisibility, - end: term.map(|term| term + self.height), id: u128::from(self.height) << 16 | u128::from(index), - limit, rune, spacers: etching.spacers, symbol: etching.symbol, + mint: etching.mint.map(|mint| MintEntry { + deadline: mint.deadline, + end: mint.term.map(|term| term + self.height), + limit: mint.limit.map(|limit| limit.clamp(0, runes::MAX_LIMIT)), + }), }), Err(_) => None, } @@ -163,18 +158,18 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> { if let Ok(key) = RuneId::try_from(id) { if let Some(entry) = self.id_to_entry.get(&key.store())? { let entry = RuneEntry::load(entry.value()); - if let Some(limit) = entry.limit { - if let Some(end) = entry.end { + if let Some(mint) = entry.mint { + if let Some(end) = mint.end { if self.height >= end { continue; } } - if let Some(deadline) = entry.deadline { + if let Some(deadline) = mint.deadline { if self.timestamp >= deadline { continue; } } - mintable.insert(id, limit); + mintable.insert(id, mint.limit.unwrap_or(runes::MAX_LIMIT)); } } } @@ -278,11 +273,9 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> { if let Some(Allocation { balance, - deadline, divisibility, - end, id, - limit, + mint, rune, spacers, symbol, @@ -300,25 +293,23 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> { id.store(), RuneEntry { burned: 0, - deadline: deadline.and_then(|deadline| (!burn).then_some(deadline)), divisibility, etching: txid, mints: 0, number, + mint: mint.and_then(|mint| (!burn).then_some(mint)), rune, spacers, - supply: if let Some(limit) = limit { - if end == Some(self.height) { + supply: if let Some(mint) = mint { + if mint.end == Some(self.height) { 0 } else { - limit + mint.limit.unwrap_or(runes::MAX_LIMIT) } } else { u128::max_value() } - balance, - end: end.and_then(|end| (!burn).then_some(end)), symbol, - limit: limit.and_then(|limit| (!burn).then_some(limit)), timestamp: self.timestamp, } .store(), diff --git a/src/lib.rs b/src/lib.rs index 0beff2e5fd..5ad0805eae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ use { pub use self::{ chain::Chain, fee_rate::FeeRate, - index::{Index, RuneEntry}, + index::{Index, MintEntry, RuneEntry}, inscriptions::{Envelope, Inscription, InscriptionId}, object::Object, options::Options, diff --git a/src/runes.rs b/src/runes.rs index 0f58905b61..3ee6a73400 100644 --- a/src/runes.rs +++ b/src/runes.rs @@ -5,16 +5,17 @@ use { pub use {edict::Edict, rune::Rune, rune_id::RuneId, runestone::Runestone}; -pub(crate) use {etching::Etching, pile::Pile, spaced_rune::SpacedRune}; +pub(crate) use {etching::Etching, mint::Mint, pile::Pile, spaced_rune::SpacedRune}; -pub const MAX_DIVISIBILITY: u8 = 38; pub(crate) const CLAIM_BIT: u128 = 1 << 48; +pub const MAX_DIVISIBILITY: u8 = 38; pub(crate) const MAX_LIMIT: u128 = 1 << 64; const RESERVED: u128 = 6402364363415443603228541259936211926; mod edict; mod etching; mod flag; +mod mint; mod pile; mod rune; mod rune_id; @@ -943,11 +944,13 @@ mod tests { }], etching: Some(Etching { rune: Some(Rune(RUNE)), - deadline: Some(1), + mint: Some(Mint { + deadline: Some(1), + limit: Some(1), + term: Some(1), + }), divisibility: 1, - limit: Some(1), symbol: Some('$'), - term: Some(1), spacers: 1, }), default_output: None, @@ -970,11 +973,9 @@ mod tests { id, RuneEntry { burned: 0, - deadline: None, divisibility: 1, - end: None, etching: txid0, - limit: None, + mint: None, mints: 0, number: 0, rune: Rune(RUNE), @@ -4258,7 +4259,10 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(1000), + mint: Some(Mint { + limit: Some(1000), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -4281,9 +4285,12 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), timestamp: 2, mints: 0, + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), ..Default::default() }, )], @@ -4314,7 +4321,10 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), supply: 1000, timestamp: 2, mints: 1, @@ -4354,7 +4364,10 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), supply: 2000, timestamp: 2, mints: 2, @@ -4392,8 +4405,11 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(1000), - term: Some(2), + mint: Some(Mint { + limit: Some(1000), + term: Some(2), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -4416,8 +4432,11 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), - end: Some(4), + mint: Some(MintEntry { + limit: Some(1000), + end: Some(4), + ..Default::default() + }), timestamp: 2, ..Default::default() }, @@ -4449,9 +4468,12 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + end: Some(4), + ..Default::default() + }), supply: 1000, - end: Some(4), timestamp: 2, mints: 1, ..Default::default() @@ -4490,10 +4512,13 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), supply: 1000, - end: Some(4), timestamp: 2, + mint: Some(MintEntry { + limit: Some(1000), + end: Some(4), + ..Default::default() + }), mints: 1, ..Default::default() }, @@ -4525,8 +4550,11 @@ mod tests { }], etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(1000), - term: Some(0), + mint: Some(Mint { + limit: Some(1000), + term: Some(0), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -4549,8 +4577,11 @@ mod tests { RuneEntry { etching: txid, rune: Rune(RUNE), - limit: Some(1000), - end: Some(2), + mint: Some(MintEntry { + limit: Some(1000), + end: Some(2), + ..Default::default() + }), timestamp: 2, ..Default::default() }, @@ -4583,9 +4614,12 @@ mod tests { RuneEntry { etching: txid, rune: Rune(RUNE), - limit: Some(1000), - end: Some(2), timestamp: 2, + mint: Some(MintEntry { + limit: Some(1000), + end: Some(2), + ..Default::default() + }), ..Default::default() }, )], @@ -4605,8 +4639,11 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(1000), - deadline: Some(4), + mint: Some(Mint { + limit: Some(1000), + deadline: Some(4), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -4627,11 +4664,14 @@ mod tests { [( id, RuneEntry { - deadline: Some(4), etching: txid0, - limit: Some(1000), rune: Rune(RUNE), timestamp: 2, + mint: Some(MintEntry { + deadline: Some(4), + limit: Some(1000), + ..Default::default() + }), ..Default::default() }, )], @@ -4660,13 +4700,16 @@ mod tests { [( id, RuneEntry { - deadline: Some(4), - etching: txid0, - limit: Some(1000), rune: Rune(RUNE), supply: 1000, timestamp: 2, mints: 1, + etching: txid0, + mint: Some(MintEntry { + deadline: Some(4), + limit: Some(1000), + ..Default::default() + }), ..Default::default() }, )], @@ -4703,10 +4746,13 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), supply: 1000, - deadline: Some(4), timestamp: 2, + mint: Some(MintEntry { + limit: Some(1000), + deadline: Some(4), + ..Default::default() + }), mints: 1, ..Default::default() }, @@ -4733,7 +4779,10 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(1000), + mint: Some(Mint { + limit: Some(1000), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -4756,7 +4805,10 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), timestamp: 2, ..Default::default() }, @@ -4789,9 +4841,12 @@ mod tests { RuneEntry { etching: txid0, rune: Rune(RUNE), - limit: Some(1000), supply: 1000, timestamp: 2, + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), mints: 1, ..Default::default() }, @@ -4827,7 +4882,10 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(1000), + mint: Some(Mint { + limit: Some(1000), + ..Default::default() + }), ..Default::default() }), edicts: vec![Edict { @@ -4855,7 +4913,10 @@ mod tests { RuneEntry { etching: txid, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), timestamp: 2, supply: 1000, ..Default::default() @@ -4866,7 +4927,7 @@ mod tests { } #[test] - fn limit_over_max_limit_is_ignored() { + fn limit_over_max_is_clamped() { let context = Context::builder().arg("--index-runes").build(); context.mine_blocks(1); @@ -4877,7 +4938,10 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(MAX_LIMIT + 1), + mint: Some(Mint { + limit: Some(MAX_LIMIT + 1), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -4901,13 +4965,18 @@ mod tests { etching, rune: Rune(RUNE), timestamp: 2, + mint: Some(MintEntry { + limit: Some(MAX_LIMIT), + deadline: None, + end: None, + }), ..Default::default() }, )], [], ); - context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 0, 0, Witness::new())], op_return: Some( Runestone { @@ -4932,10 +5001,17 @@ mod tests { etching, rune: Rune(RUNE), timestamp: 2, + mints: 1, + supply: MAX_LIMIT, + mint: Some(MintEntry { + limit: Some(MAX_LIMIT), + deadline: None, + end: None, + }), ..Default::default() }, )], - [], + [(OutPoint { txid, vout: 0 }, vec![(id, MAX_LIMIT)])], ); } @@ -4951,7 +5027,10 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - term: Some(1), + mint: Some(Mint { + term: Some(1), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -4974,8 +5053,11 @@ mod tests { RuneEntry { etching, rune: Rune(RUNE), - limit: Some(MAX_LIMIT), - end: Some(3), + mint: Some(MintEntry { + limit: None, + end: Some(3), + ..Default::default() + }), timestamp: 2, ..Default::default() }, @@ -4996,7 +5078,10 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(1000), + mint: Some(Mint { + limit: Some(1000), + ..Default::default() + }), ..Default::default() }), edicts: vec![Edict { @@ -5024,7 +5109,10 @@ mod tests { RuneEntry { etching, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), timestamp: 2, supply: 1000, ..Default::default() @@ -5068,7 +5156,10 @@ mod tests { RuneEntry { etching, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), timestamp: 2, supply: 2000, mints: 1, @@ -5106,7 +5197,10 @@ mod tests { Runestone { etching: Some(Etching { rune: Some(Rune(RUNE)), - limit: Some(1000), + mint: Some(Mint { + limit: Some(1000), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -5129,7 +5223,10 @@ mod tests { RuneEntry { etching, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), timestamp: 2, ..Default::default() }, @@ -5178,7 +5275,10 @@ mod tests { RuneEntry { etching, rune: Rune(RUNE), - limit: Some(1000), + mint: Some(MintEntry { + limit: Some(1000), + ..Default::default() + }), timestamp: 2, supply: 1000, mints: 1, diff --git a/src/runes/etching.rs b/src/runes/etching.rs index 75e1344f55..1f051148d8 100644 --- a/src/runes/etching.rs +++ b/src/runes/etching.rs @@ -2,11 +2,9 @@ use super::*; #[derive(Default, Serialize, Debug, PartialEq, Copy, Clone)] pub struct Etching { - pub deadline: Option, pub divisibility: u8, - pub limit: Option, + pub mint: Option, pub rune: Option, pub spacers: u32, pub symbol: Option, - pub term: Option, } diff --git a/src/runes/flag.rs b/src/runes/flag.rs index fcc39e93b9..e0c9f93160 100644 --- a/src/runes/flag.rs +++ b/src/runes/flag.rs @@ -1,5 +1,6 @@ pub(super) enum Flag { Etch = 0, + Mint = 1, #[allow(unused)] Burn = 127, } diff --git a/src/runes/mint.rs b/src/runes/mint.rs new file mode 100644 index 0000000000..5632d9b023 --- /dev/null +++ b/src/runes/mint.rs @@ -0,0 +1,8 @@ +use super::*; + +#[derive(Default, Serialize, Debug, PartialEq, Copy, Clone)] +pub struct Mint { + pub deadline: Option, + pub limit: Option, + pub term: Option, +} diff --git a/src/runes/runestone.rs b/src/runes/runestone.rs index c56a41d2dc..15ec1e909e 100644 --- a/src/runes/runestone.rs +++ b/src/runes/runestone.rs @@ -77,7 +77,7 @@ impl Runestone { let limit = Tag::Limit .take(&mut fields) - .and_then(|limit| (limit <= MAX_LIMIT).then_some(limit)); + .map(|limit| limit.clamp(0, MAX_LIMIT)); let rune = Tag::Rune.take(&mut fields).map(Rune); @@ -100,15 +100,19 @@ impl Runestone { let etch = Flag::Etch.take(&mut flags); + let mint = Flag::Mint.take(&mut flags); + let etching = if etch { Some(Etching { - deadline, divisibility, - limit, rune, spacers, symbol, - term, + mint: mint.then_some(Mint { + deadline, + limit, + term, + }), }) } else { None @@ -129,16 +133,16 @@ impl Runestone { let mut flags = 0; Flag::Etch.set(&mut flags); + if etching.mint.is_some() { + Flag::Mint.set(&mut flags); + } + Tag::Flags.encode(flags, &mut payload); if let Some(rune) = etching.rune { Tag::Rune.encode(rune.0, &mut payload); } - if let Some(deadline) = etching.deadline { - Tag::Deadline.encode(deadline.into(), &mut payload); - } - if etching.divisibility != 0 { Tag::Divisibility.encode(etching.divisibility.into(), &mut payload); } @@ -151,12 +155,18 @@ impl Runestone { Tag::Symbol.encode(symbol.into(), &mut payload); } - if let Some(limit) = etching.limit { - Tag::Limit.encode(limit, &mut payload); - } + if let Some(mint) = etching.mint { + if let Some(deadline) = mint.deadline { + Tag::Deadline.encode(deadline.into(), &mut payload); + } - if let Some(term) = etching.term { - Tag::Term.encode(term.into(), &mut payload); + if let Some(limit) = mint.limit { + Tag::Limit.encode(limit, &mut payload); + } + + if let Some(term) = mint.term { + Tag::Term.encode(term.into(), &mut payload); + } } } @@ -557,12 +567,36 @@ mod tests { ); } + #[test] + fn etch_flag_is_required_to_etch_rune_even_if_mint_is_set() { + assert_eq!( + decipher(&[ + Tag::Flags.into(), + Flag::Mint.mask(), + Tag::Term.into(), + 4, + Tag::Body.into(), + 1, + 2, + 3 + ]), + Runestone { + edicts: vec![Edict { + id: 1, + amount: 2, + output: 3, + }], + ..Default::default() + }, + ); + } + #[test] fn decipher_etching_with_term() { assert_eq!( decipher(&[ Tag::Flags.into(), - Flag::Etch.mask(), + Flag::Etch.mask() | Flag::Mint.mask(), Tag::Term.into(), 4, Tag::Body.into(), @@ -577,7 +611,10 @@ mod tests { output: 3, }], etching: Some(Etching { - term: Some(4), + mint: Some(Mint { + term: Some(4), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -590,7 +627,7 @@ mod tests { assert_eq!( decipher(&[ Tag::Flags.into(), - Flag::Etch.mask(), + Flag::Etch.mask() | Flag::Mint.mask(), Tag::Limit.into(), 4, Tag::Body.into(), @@ -605,7 +642,10 @@ mod tests { output: 3, }], etching: Some(Etching { - limit: Some(4), + mint: Some(Mint { + limit: Some(4), + ..Default::default() + }), ..Default::default() }), ..Default::default() @@ -860,7 +900,7 @@ mod tests { assert_eq!( decipher(&[ Tag::Flags.into(), - Flag::Etch.mask(), + Flag::Etch.mask() | Flag::Mint.mask(), Tag::Rune.into(), 4, Tag::Deadline.into(), @@ -888,11 +928,13 @@ mod tests { }], etching: Some(Etching { rune: Some(Rune(4)), - deadline: Some(7), + mint: Some(Mint { + deadline: Some(7), + term: Some(2), + limit: Some(3), + }), divisibility: 1, symbol: Some('a'), - term: Some(2), - limit: Some(3), spacers: 5, }), ..Default::default() @@ -1213,12 +1255,14 @@ mod tests { Vec::new(), Some(Etching { divisibility: MAX_DIVISIBILITY, - deadline: Some(10000), + mint: Some(Mint { + deadline: Some(10000), + limit: Some(1), + term: Some(1), + }), rune: Some(Rune(0)), symbol: Some('$'), - limit: Some(1), spacers: 1, - term: Some(1), }), 19, ); @@ -1491,11 +1535,13 @@ mod tests { Runestone { etching: Some(Etching { divisibility: 1, - deadline: Some(2), - limit: Some(3), + mint: Some(Mint { + deadline: Some(2), + limit: Some(3), + term: Some(5), + }), symbol: Some('@'), rune: Some(Rune(4)), - term: Some(5), spacers: 6, }), edicts: vec![ @@ -1515,17 +1561,17 @@ mod tests { }, &[ Tag::Flags.into(), - Flag::Etch.mask(), + Flag::Etch.mask() | Flag::Mint.mask(), Tag::Rune.into(), 4, - Tag::Deadline.into(), - 2, Tag::Divisibility.into(), 1, Tag::Spacers.into(), 6, Tag::Symbol.into(), '@'.into(), + Tag::Deadline.into(), + 2, Tag::Limit.into(), 3, Tag::Term.into(), @@ -1546,11 +1592,9 @@ mod tests { Runestone { etching: Some(Etching { divisibility: 0, - deadline: None, - limit: None, + mint: None, symbol: None, rune: Some(Rune(3)), - term: None, spacers: 0, }), burn: false, @@ -1563,11 +1607,9 @@ mod tests { Runestone { etching: Some(Etching { divisibility: 0, - deadline: None, - limit: None, + mint: None, symbol: None, rune: None, - term: None, spacers: 0, }), burn: false, diff --git a/src/subcommand/runes.rs b/src/subcommand/runes.rs index 854d04a37e..311229aa5d 100644 --- a/src/subcommand/runes.rs +++ b/src/subcommand/runes.rs @@ -8,14 +8,12 @@ pub struct Output { #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct RuneInfo { pub burned: u128, - pub deadline: Option, pub divisibility: u8, - pub end: Option, pub etching: Txid, pub height: u32, pub id: RuneId, pub index: u16, - pub limit: Option, + pub mint: Option, pub mints: u64, pub number: u64, pub rune: Rune, @@ -44,11 +42,9 @@ pub(crate) fn run(options: Options) -> SubcommandResult { id, RuneEntry { burned, - deadline, divisibility, - end, etching, - limit, + mint, mints, number, rune, @@ -62,14 +58,12 @@ pub(crate) fn run(options: Options) -> SubcommandResult { rune, RuneInfo { burned, - deadline, divisibility, - end, etching, height: id.height, id, index: id.index, - limit, + mint, mints, number, rune, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index d5d768a9c9..0a499b2702 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2353,8 +2353,8 @@ mod tests {
2
etching transaction index
1
-
mints
-
0
+
mint
+
no
supply
340282366920938463463374607431768211455\u{00A0}%
burned
diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index 8e5d38d5e1..d3dc5659a3 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -59,13 +59,11 @@ impl Etch { let runestone = Runestone { etching: Some(Etching { - deadline: None, divisibility: self.divisibility, - limit: None, + mint: None, rune: Some(rune), spacers, symbol: Some(self.symbol), - term: None, }), edicts: vec![Edict { amount: self.supply.to_amount(self.divisibility)?, diff --git a/src/templates/rune.rs b/src/templates/rune.rs index 78a688a66a..83651b4045 100644 --- a/src/templates/rune.rs +++ b/src/templates/rune.rs @@ -25,13 +25,15 @@ mod tests { RuneHtml { entry: RuneEntry { burned: 123456789123456789, - deadline: Some(7), divisibility: 9, - end: Some(11), etching: Txid::all_zeros(), - limit: Some(1000000001), mints: 100, number: 25, + mint: Some(MintEntry { + end: Some(11), + limit: Some(1000000001), + deadline: Some(7), + }), rune: Rune(u128::max_value()), spacers: 1, supply: 123456789123456789, @@ -60,14 +62,19 @@ mod tests {
10
etching transaction index
9
-
open etching end
-
11
-
open etching deadline
-
-
open etching limit
-
1.000000001\u{00A0}%
-
mints
-
100
+
mint
+
+
+
deadline
+
+
end
+
11
+
limit
+
1.000000001 %
+
mints
+
100
+
+
supply
123456789.123456789\u{00A0}%
burned
@@ -86,16 +93,14 @@ mod tests { } #[test] - fn display_reduced() { + fn display_no_mint() { assert_regex_match!( RuneHtml { entry: RuneEntry { burned: 123456789123456789, - deadline: None, + mint: None, divisibility: 9, - end: None, etching: Txid::all_zeros(), - limit: None, mints: 0, number: 25, rune: Rune(u128::max_value()), @@ -122,8 +127,75 @@ mod tests {
10
etching transaction index
9
-
mints
-
0
+
mint
+
no
+
supply
+
123456789.123456789\u{00A0}%
+
burned
+
123456789.123456789\u{00A0}%
+
divisibility
+
9
+
symbol
+
%
+
etching
+
0{64}
+ +" + ); + } + + #[test] + fn display_empty_mint() { + assert_regex_match!( + RuneHtml { + entry: RuneEntry { + burned: 123456789123456789, + mint: Some(MintEntry { + deadline: None, + end: None, + limit: None, + }), + divisibility: 9, + etching: Txid::all_zeros(), + mints: 0, + number: 25, + rune: Rune(u128::max_value()), + spacers: 1, + supply: 123456789123456789, + symbol: Some('%'), + timestamp: 0, + }, + id: RuneId { + height: 10, + index: 9, + }, + parent: None, + }, + "

B•CGDENLQRQWDSLRUGSNLBTMFIJAV

+
+
number
+
25
+
timestamp
+
+
id
+
10/9
+
etching block height
+
10
+
etching transaction index
+
9
+
mint
+
+
+
deadline
+
none
+
end
+
none
+
limit
+
none
+
mints
+
0
+
+
supply
123456789.123456789\u{00A0}%
burned
diff --git a/templates/rune.html b/templates/rune.html index 5635b8cd74..89ffb10ce6 100644 --- a/templates/rune.html +++ b/templates/rune.html @@ -13,20 +13,35 @@

{{ self.entry.spaced_rune() }}

{{ self.id.height }}
etching transaction index
{{ self.id.index }}
-%% if let Some(end) = self.entry.end { -
open etching end
-
{{ end }}
+
mint
+%% if let Some(mint) = self.entry.mint { +
+
+
deadline
+%% if let Some(deadline) = mint.deadline { +
+%% } else { +
none
%% } -%% if let Some(deadline) = self.entry.deadline { -
open etching deadline
-
+
end
+%% if let Some(end) = mint.end { +
{{ end }}
+%% } else { +
none
%% } -%% if let Some(limit) = self.entry.limit { -
open etching limit
-
{{ Pile{ amount: limit, divisibility: self.entry.divisibility, symbol: self.entry.symbol } }}
+
limit
+%% if let Some(limit) = mint.limit { +
{{ Pile{ amount: limit, divisibility: self.entry.divisibility, symbol: self.entry.symbol } }}
+%% } else { +
none
+%% } +
mints
+
{{ self.entry.mints }}
+
+
+%% } else { +
no
%% } -
mints
-
{{ self.entry.mints }}
supply
{{ Pile{ amount: self.entry.supply, divisibility: self.entry.divisibility, symbol: self.entry.symbol } }}
burned
diff --git a/tests/etch.rs b/tests/etch.rs index 2b9c5edef5..c66cf121b5 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -174,15 +174,14 @@ fn runes_can_be_etched() { bitcoin_rpc_server.mine_blocks(1); - assert_eq!( + pretty_assert_eq!( runes(&bitcoin_rpc_server), vec![( Rune(RUNE), RuneInfo { burned: 0, - deadline: None, + mint: None, divisibility: 1, - end: None, etching: output.transaction, height: 2, id: RuneId { @@ -190,7 +189,6 @@ fn runes_can_be_etched() { index: 1 }, index: 1, - limit: None, mints: 0, number: 0, rune: Rune(RUNE), diff --git a/tests/json_api.rs b/tests/json_api.rs index 03f699d0d6..a0c0c4fd90 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -537,11 +537,9 @@ fn get_runes() { RuneJson { entry: RuneEntry { burned: 0, - deadline: None, + mint: None, divisibility: 0, - end: None, etching: a.transaction, - limit: None, mints: 0, number: 0, rune: Rune(RUNE), @@ -575,11 +573,9 @@ fn get_runes() { }, RuneEntry { burned: 0, - deadline: None, + mint: None, divisibility: 0, - end: None, etching: a.transaction, - limit: None, mints: 0, number: 0, rune: Rune(RUNE), @@ -596,11 +592,9 @@ fn get_runes() { }, RuneEntry { burned: 0, - deadline: None, + mint: None, divisibility: 0, - end: None, etching: b.transaction, - limit: None, mints: 0, number: 1, rune: Rune(RUNE + 1), @@ -617,11 +611,9 @@ fn get_runes() { }, RuneEntry { burned: 0, - deadline: None, + mint: None, divisibility: 0, - end: None, etching: c.transaction, - limit: None, mints: 0, number: 2, rune: Rune(RUNE + 2), diff --git a/tests/runes.rs b/tests/runes.rs index 4437089781..341279ad70 100644 --- a/tests/runes.rs +++ b/tests/runes.rs @@ -54,9 +54,8 @@ fn one_rune() { Rune(RUNE), RuneInfo { burned: 0, - deadline: None, + mint: None, divisibility: 0, - end: None, etching: etch.transaction, height: 2, id: RuneId { @@ -64,7 +63,6 @@ fn one_rune() { index: 1 }, index: 1, - limit: None, mints: 0, number: 0, rune: Rune(RUNE), @@ -94,7 +92,7 @@ fn two_runes() { let a = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let b = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); - assert_eq!( + pretty_assert_eq!( CommandBuilder::new("--index-runes --regtest runes") .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::(), @@ -104,9 +102,8 @@ fn two_runes() { Rune(RUNE), RuneInfo { burned: 0, - deadline: None, + mint: None, divisibility: 0, - end: None, etching: a.transaction, height: 2, id: RuneId { @@ -114,7 +111,6 @@ fn two_runes() { index: 1 }, index: 1, - limit: None, mints: 0, number: 0, rune: Rune(RUNE), @@ -128,9 +124,8 @@ fn two_runes() { Rune(RUNE + 1), RuneInfo { burned: 0, - deadline: None, + mint: None, divisibility: 0, - end: None, etching: b.transaction, height: 4, id: RuneId { @@ -138,7 +133,6 @@ fn two_runes() { index: 1 }, index: 1, - limit: None, mints: 0, number: 1, rune: Rune(RUNE + 1),