From 5de81cf953dcceb5e9e2382b582d989495422872 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Fri, 29 Mar 2024 14:19:39 -0700 Subject: [PATCH] Derive reserved rune names from rune ID (#3412) --- crates/ordinals/src/rune.rs | 19 ++++++++++++------- docs/src/runes/specification.md | 20 +++++++++++++++----- src/index/updater/rune_updater.rs | 2 +- src/runes.rs | 14 +++++++------- tests/wallet/batch_command.rs | 2 +- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/crates/ordinals/src/rune.rs b/crates/ordinals/src/rune.rs index 0c144188a7..d3cd3c49d2 100644 --- a/crates/ordinals/src/rune.rs +++ b/crates/ordinals/src/rune.rs @@ -88,8 +88,12 @@ impl Rune { self.0 >= Self::RESERVED } - pub fn reserved(n: u128) -> Option { - Some(Rune(Self::RESERVED.checked_add(n)?)) + pub fn reserved(block: u64, tx: u32) -> Self { + Self( + Self::RESERVED + .checked_add(u128::from(block) << 32 | u128::from(tx)) + .unwrap(), + ) } pub fn commitment(self) -> Vec { @@ -360,13 +364,14 @@ mod tests { "AAAAAAAAAAAAAAAAAAAAAAAAAAA".parse::().unwrap().0, ); - assert_eq!(Rune::reserved(0), Some(Rune(Rune::RESERVED))); - assert_eq!(Rune::reserved(1), Some(Rune(Rune::RESERVED + 1))); + assert_eq!(Rune::reserved(0, 0), Rune(Rune::RESERVED)); + assert_eq!(Rune::reserved(0, 1), Rune(Rune::RESERVED + 1)); + assert_eq!(Rune::reserved(1, 0), Rune(Rune::RESERVED + (1 << 32))); + assert_eq!(Rune::reserved(1, 1), Rune(Rune::RESERVED + (1 << 32) + 1)); assert_eq!( - Rune::reserved(u128::MAX - Rune::RESERVED), - Some(Rune(u128::MAX)) + Rune::reserved(u64::MAX, u32::MAX), + Rune(Rune::RESERVED + (u128::from(u64::MAX) << 32 | u128::from(u32::MAX))), ); - assert_eq!(Rune::reserved(u128::MAX - Rune::RESERVED + 1), None); } #[test] diff --git a/docs/src/runes/specification.md b/docs/src/runes/specification.md index 2875e30b54..9639ddfac8 100644 --- a/docs/src/runes/specification.md +++ b/docs/src/runes/specification.md @@ -276,9 +276,8 @@ runestone is a cenotaph. ##### Rune The `Rune` field contains the name of the rune being etched. If the `Etching` -flag is set, but the `Rune` field is omitted, a reserved rune name is -allocated, starting with `AAAAAAAAAAAAAAAAAAAAAAAAAAA` and increasing by one -with each such reserved rune allocated. +flag is set but the `Rune` field is omitted, a reserved rune name is +allocated. ##### Premine @@ -428,8 +427,19 @@ And so on and so on. Rune names `AAAAAAAAAAAAAAAAAAAAAAAAAAA` and above are reserved. -`rune` may be omitted, in which case the first unallocated rune name, starting -at `AAAAAAAAAAAAAAAAAAAAAAAAAAA` and increasing by one each time, is etched. +If `rune` is omitted a reserved rune name is allocated as follows: + +```rust +fn reserve(block: u64, tx: u32) -> Rune { + Rune( + 6402364363415443603228541259936211926 + + (u128::from(block) << 32 | u128::from(tx)) + ) +} +``` + +`6402364363415443603228541259936211926` corresponds to the rune name +`AAAAAAAAAAAAAAAAAAAAAAAAAAA`. If `rune` is present, it must be unlocked as of the block in which the etching appears. diff --git a/src/index/updater/rune_updater.rs b/src/index/updater/rune_updater.rs index a1eff1e6a5..8490dacf7e 100644 --- a/src/index/updater/rune_updater.rs +++ b/src/index/updater/rune_updater.rs @@ -308,7 +308,7 @@ impl<'a, 'tx, 'client> RuneUpdater<'a, 'tx, 'client> { .statistic_to_count .insert(&Statistic::ReservedRunes.into(), reserved_runes + 1)?; - Rune::reserved(reserved_runes.into()).unwrap() + Rune::reserved(self.height.into(), tx_index) }; Ok(Some(Etched { diff --git a/src/runes.rs b/src/runes.rs index 5352e8b36f..39386b5290 100644 --- a/src/runes.rs +++ b/src/runes.rs @@ -230,7 +230,7 @@ mod tests { output: 0, }], etching: Some(Etching { - rune: Some(Rune::reserved(0).unwrap()), + rune: Some(Rune::reserved(0, 0)), ..default() }), ..default() @@ -252,7 +252,7 @@ mod tests { output: 0, }], etching: Some(Etching { - rune: Some(Rune(Rune::reserved(0).unwrap().n() - 1)), + rune: Some(Rune(Rune::reserved(0, 0).n() - 1)), premine: Some(u128::MAX), ..default() }), @@ -268,7 +268,7 @@ mod tests { block: id.block, etching: txid, spaced_rune: SpacedRune { - rune: Rune(Rune::reserved(0).unwrap().n() - 1), + rune: Rune(Rune::reserved(0, 0).n() - 1), spacers: 0, }, premine: u128::MAX, @@ -320,7 +320,7 @@ mod tests { block: id0.block, etching: txid0, spaced_rune: SpacedRune { - rune: Rune::reserved(0).unwrap(), + rune: Rune::reserved(id0.block, id0.tx), spacers: 0, }, premine: u128::MAX, @@ -372,7 +372,7 @@ mod tests { block: id0.block, etching: txid0, spaced_rune: SpacedRune { - rune: Rune::reserved(0).unwrap(), + rune: Rune::reserved(id0.block, id0.tx), spacers: 0, }, premine: u128::MAX, @@ -386,7 +386,7 @@ mod tests { block: id1.block, etching: txid1, spaced_rune: SpacedRune { - rune: Rune::reserved(1).unwrap(), + rune: Rune::reserved(id1.block, id0.tx), spacers: 0, }, premine: u128::MAX, @@ -907,7 +907,7 @@ mod tests { block: id.block, etching: txid0, spaced_rune: SpacedRune { - rune: Rune::reserved(0).unwrap(), + rune: Rune::reserved(2, 1), spacers: 0, }, timestamp: id.block, diff --git a/tests/wallet/batch_command.rs b/tests/wallet/batch_command.rs index 1341ca9814..bd46adf0fe 100644 --- a/tests/wallet/batch_command.rs +++ b/tests/wallet/batch_command.rs @@ -1707,7 +1707,7 @@ fn etch_reserved_rune_error() { etching: Some(batch::Etching { divisibility: 0, rune: SpacedRune { - rune: Rune::reserved(0).unwrap(), + rune: Rune::reserved(0, 0), spacers: 0, }, premine: "1000".parse().unwrap(),