From 1d6a2426180703802ff932c3bf2cde88cb90fc92 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 15 Oct 2023 13:41:26 -0700 Subject: [PATCH] Delta encode Rune IDs in edicts (#2532) --- src/runes/runestone.rs | 68 +++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/src/runes/runestone.rs b/src/runes/runestone.rs index 8f6ebad0c0..6640c91dd9 100644 --- a/src/runes/runestone.rs +++ b/src/runes/runestone.rs @@ -27,10 +27,13 @@ impl Runestone { let mut edicts = Vec::new(); let mut etching = None; - + let mut id = 0u128; for chunk in integers.chunks(3) { match *chunk { - [id, amount, output] => edicts.push(Edict { id, amount, output }), + [id_delta, amount, output] => { + id = id.saturating_add(id_delta); + edicts.push(Edict { id, amount, output }); + } [rune] => { etching = Some(Etching { divisibility: 0, @@ -56,10 +59,15 @@ impl Runestone { pub(crate) fn encipher(&self) -> ScriptBuf { let mut payload = Vec::new(); - for edict in &self.edicts { - varint::encode_to_vec(edict.id, &mut payload); + let mut edicts = self.edicts.clone(); + edicts.sort_by_key(|edict| edict.id); + + let mut id = 0; + for edict in edicts { + varint::encode_to_vec(edict.id - id, &mut payload); varint::encode_to_vec(edict.amount, &mut payload); varint::encode_to_vec(edict.output, &mut payload); + id = edict.id; } if let Some(etching) = self.etching { @@ -529,7 +537,7 @@ mod tests { #[test] fn runestone_may_contain_multiple_edicts() { - let payload = payload(&[1, 2, 3, 4, 5, 6]); + let payload = payload(&[1, 2, 3, 3, 5, 6]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -565,6 +573,44 @@ mod tests { ); } + #[test] + fn id_deltas_saturate_to_max() { + let payload = payload(&[1, 2, 3, u128::max_value(), 5, 6]); + + let payload: &PushBytes = payload.as_slice().try_into().unwrap(); + + assert_eq!( + Runestone::decipher(&Transaction { + input: Vec::new(), + output: vec![TxOut { + script_pubkey: script::Builder::new() + .push_opcode(opcodes::all::OP_RETURN) + .push_slice(b"RUNE_TEST") + .push_slice(payload) + .into_script(), + value: 0 + }], + lock_time: locktime::absolute::LockTime::ZERO, + version: 0, + }), + Ok(Some(Runestone { + edicts: vec![ + Edict { + id: 1, + amount: 2, + output: 3, + }, + Edict { + id: u128::max_value(), + amount: 5, + output: 6, + }, + ], + etching: None, + })) + ); + } + #[test] fn payload_pushes_are_concatenated() { assert_eq!( @@ -768,7 +814,7 @@ mod tests { }, ], None, - 54, + 49, ); case( @@ -802,7 +848,7 @@ mod tests { }, ], None, - 81, + 70, ); case( @@ -819,7 +865,7 @@ mod tests { 4 ], None, - 70, + 55, ); case( @@ -836,7 +882,7 @@ mod tests { 5 ], None, - 88, + 67, ); case( @@ -853,7 +899,7 @@ mod tests { 5 ], None, - 72, + 64, ); case( @@ -870,7 +916,7 @@ mod tests { 5 ], None, - 83, + 62, ); } }