From c738cb25ad6e320df37f08d9fa8f326a71b39c7f Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sun, 15 Oct 2023 17:15:12 -0700 Subject: [PATCH 1/7] Encode runestone using tags --- src/index/updater/rune_updater.rs | 103 ++++---- src/lib.rs | 2 +- src/runes.rs | 255 +++++++++++++++---- src/runes/runestone.rs | 392 ++++++++++++++++++++++-------- src/subcommand/server.rs | 3 + 5 files changed, 568 insertions(+), 187 deletions(-) diff --git a/src/index/updater/rune_updater.rs b/src/index/updater/rune_updater.rs index 2c2c2f11be..0065328387 100644 --- a/src/index/updater/rune_updater.rs +++ b/src/index/updater/rune_updater.rs @@ -59,6 +59,11 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> { } } + let burn = runestone + .as_ref() + .map(|runestone| runestone.burn) + .unwrap_or_default(); + // A vector of allocated transaction output rune balances let mut allocated: Vec> = vec![HashMap::new(); tx.output.len()]; @@ -90,42 +95,44 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> { None => None, }; - for Edict { id, amount, output } in runestone.edicts { - // Skip edicts not referring to valid outputs - if output >= tx.output.len() as u128 { - continue; - } - - let (balance, id) = if id == 0 { - // If this edict allocates new issuance runes, skip it - // if no issuance was present, or if the issuance was invalid. - // Additionally, replace ID 0 with the newly assigned ID, and - // get the unallocated balance of the issuance. - match allocation.as_mut() { - Some(Allocation { balance, id, .. }) => (balance, *id), - None => continue, + if !burn { + for Edict { id, amount, output } in runestone.edicts { + // Skip edicts not referring to valid outputs + if output >= tx.output.len() as u128 { + continue; } - } else { - // Get the unallocated balance of the given ID - match unallocated.get_mut(&id) { - Some(balance) => (balance, id), - None => continue, + + let (balance, id) = if id == 0 { + // If this edict allocates new issuance runes, skip it + // if no issuance was present, or if the issuance was invalid. + // Additionally, replace ID 0 with the newly assigned ID, and + // get the unallocated balance of the issuance. + match allocation.as_mut() { + Some(Allocation { balance, id, .. }) => (balance, *id), + None => continue, + } + } else { + // Get the unallocated balance of the given ID + match unallocated.get_mut(&id) { + Some(balance) => (balance, id), + None => continue, + } + }; + + // Get the allocatable amount + let amount = if amount == 0 { + *balance + } else { + amount.min(*balance) + }; + + // If the amount to be allocated is greater than zero, + // deduct it from the remaining balance, and increment + // the allocated entry. + if amount > 0 { + *balance -= amount; + *allocated[output as usize].entry(id).or_default() += amount; } - }; - - // Get the allocatable amount - let amount = if amount == 0 { - *balance - } else { - amount.min(*balance) - }; - - // If the amount to be allocated is greater than zero, - // deduct it from the remaining balance, and increment - // the allocated entry. - if amount > 0 { - *balance -= amount; - *allocated[output as usize].entry(id).or_default() += amount; } } @@ -154,22 +161,28 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> { } } - // Assign all un-allocated runes to the first non OP_RETURN output - if let Some((vout, _)) = tx - .output - .iter() - .enumerate() - .find(|(_, tx_out)| !tx_out.script_pubkey.is_op_return()) - { + let mut burned: HashMap = HashMap::new(); + + if burn { for (id, balance) in unallocated { - if balance > 0 { - *allocated[vout].entry(id).or_default() += balance; + *burned.entry(id).or_default() += balance; + } + } else { + // Assign all un-allocated runes to the first non OP_RETURN output + if let Some((vout, _)) = tx + .output + .iter() + .enumerate() + .find(|(_, tx_out)| !tx_out.script_pubkey.is_op_return()) + { + for (id, balance) in unallocated { + if balance > 0 { + *allocated[vout].entry(id).or_default() += balance; + } } } } - let mut burned: HashMap = HashMap::new(); - // update outpoint balances let mut buffer: Vec = Vec::new(); for (vout, balances) in allocated.into_iter().enumerate() { diff --git a/src/lib.rs b/src/lib.rs index 7ccc0b30f8..5f7c024c3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,7 +55,7 @@ use { serde::{Deserialize, Deserializer, Serialize, Serializer}, std::{ cmp, - collections::{BTreeMap, HashSet, VecDeque}, + collections::{BTreeMap, HashMap, HashSet, VecDeque}, env, ffi::OsString, fmt::{self, Display, Formatter}, diff --git a/src/runes.rs b/src/runes.rs index fbd3ca21ac..f4fa16f33f 100644 --- a/src/runes.rs +++ b/src/runes.rs @@ -60,13 +60,7 @@ mod tests { context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0, Witness::new())], - op_return: Some( - Runestone { - edicts: Vec::new(), - etching: None, - } - .encipher(), - ), + op_return: Some(Runestone::default().encipher()), ..Default::default() }); @@ -88,12 +82,12 @@ mod tests { inputs: &[(1, 0, 0, Witness::new())], op_return: Some( Runestone { - edicts: Vec::new(), etching: Some(Etching { divisibility: 0, rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -147,6 +141,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -204,6 +199,7 @@ mod tests { rune: Rune(u128::from(Sat::SUPPLY - 150 * COIN_VALUE - 1)), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -238,6 +234,7 @@ mod tests { rune: Rune(u128::from(Sat::SUPPLY - 150 * COIN_VALUE)), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -295,6 +292,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -358,6 +356,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -421,6 +420,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -477,6 +477,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -540,6 +541,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -606,6 +608,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -662,6 +665,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -710,7 +714,7 @@ mod tests { amount: u128::max_value(), output: 0, }], - etching: None, + ..Default::default() } .encipher(), ), @@ -747,7 +751,7 @@ mod tests { } #[test] - fn unallocated_runes_are_assigned_to_first_non_op_return_output() { + fn etching_rune_is_burned_if_an_unrecognized_even_tag_is_encountered() { let context = Context::builder() .arg("--index-runes-pre-alpha-i-agree-to-get-rekt") .build(); @@ -768,6 +772,61 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + burn: true, + } + .encipher(), + ), + ..Default::default() + }); + + context.mine_blocks(1); + + let id = RuneId { + height: 2, + index: 1, + }; + + assert_eq!( + context.index.runes().unwrap().unwrap(), + [( + id, + RuneEntry { + burned: 0, + divisibility: 0, + etching: txid0, + rune: Rune(RUNE), + supply: 0, + symbol: None, + } + )] + ); + + assert_eq!(context.index.get_rune_balances(), []); + } + + #[test] + fn input_runes_are_burned_if_an_unrecognized_even_tag_is_encountered() { + let context = Context::builder() + .arg("--index-runes-pre-alpha-i-agree-to-get-rekt") + .build(); + + context.mine_blocks(1); + + let txid0 = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0, Witness::new())], + op_return: Some( + Runestone { + edicts: vec![Edict { + id: 0, + amount: u128::max_value(), + output: 0, + }], + etching: Some(Etching { + divisibility: 0, + rune: Rune(RUNE), + symbol: None, + }), + ..Default::default() } .encipher(), ), @@ -807,12 +866,61 @@ mod tests { )] ); - let txid1 = context.rpc_server.broadcast_tx(TransactionTemplate { + context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 1, 0, Witness::new())], op_return: Some( Runestone { - edicts: Vec::new(), - etching: None, + burn: true, + ..Default::default() + } + .encipher(), + ), + ..Default::default() + }); + + context.mine_blocks(1); + + assert_eq!( + context.index.runes().unwrap().unwrap(), + [( + id, + RuneEntry { + burned: u128::max_value(), + divisibility: 0, + etching: txid0, + rune: Rune(RUNE), + supply: u128::max_value(), + symbol: None, + } + )] + ); + + assert_eq!(context.index.get_rune_balances(), []); + } + + #[test] + fn unallocated_runes_are_assigned_to_first_non_op_return_output() { + let context = Context::builder() + .arg("--index-runes-pre-alpha-i-agree-to-get-rekt") + .build(); + + context.mine_blocks(1); + + let txid0 = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0, Witness::new())], + op_return: Some( + Runestone { + edicts: vec![Edict { + id: 0, + amount: u128::max_value(), + output: 0, + }], + etching: Some(Etching { + divisibility: 0, + rune: Rune(RUNE), + symbol: None, + }), + ..Default::default() } .encipher(), ), @@ -821,6 +929,45 @@ mod tests { context.mine_blocks(1); + let id = RuneId { + height: 2, + index: 1, + }; + + assert_eq!( + context.index.runes().unwrap().unwrap(), + [( + id, + RuneEntry { + burned: 0, + divisibility: 0, + etching: txid0, + rune: Rune(RUNE), + supply: u128::max_value(), + symbol: None, + } + )] + ); + + assert_eq!( + context.index.get_rune_balances(), + [( + OutPoint { + txid: txid0, + vout: 0 + }, + vec![(id, u128::max_value())] + )] + ); + + let txid1 = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(2, 1, 0, Witness::new())], + op_return: Some(Runestone::default().encipher()), + ..Default::default() + }); + + context.mine_blocks(1); + assert_eq!( context.index.runes().unwrap().unwrap(), [( @@ -871,6 +1018,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -967,6 +1115,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1014,6 +1163,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1065,6 +1215,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1118,6 +1269,7 @@ mod tests { rune: Rune(RUNE + 1), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1164,17 +1316,17 @@ mod tests { [ ( OutPoint { - txid: txid0, + txid: txid1, vout: 0 }, - vec![(id0, u128::max_value())] + vec![(id1, u128::max_value())] ), ( OutPoint { - txid: txid1, + txid: txid0, vout: 0 }, - vec![(id1, u128::max_value())] + vec![(id0, u128::max_value())] ), ] ); @@ -1248,6 +1400,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1301,6 +1454,7 @@ mod tests { rune: Rune(RUNE + 1), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1347,17 +1501,17 @@ mod tests { [ ( OutPoint { - txid: txid0, + txid: txid1, vout: 0 }, - vec![(id0, u128::max_value())] + vec![(id1, u128::max_value())] ), ( OutPoint { - txid: txid1, + txid: txid0, vout: 0 }, - vec![(id1, u128::max_value())] + vec![(id0, u128::max_value())] ), ] ); @@ -1425,7 +1579,7 @@ mod tests { output: 1, }, ], - etching: None, + ..Default::default() } .encipher(), ), @@ -1508,6 +1662,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1561,6 +1716,7 @@ mod tests { rune: Rune(RUNE + 1), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1607,17 +1763,17 @@ mod tests { [ ( OutPoint { - txid: txid0, + txid: txid1, vout: 0 }, - vec![(id0, u128::max_value())] + vec![(id1, u128::max_value())] ), ( OutPoint { - txid: txid1, + txid: txid0, vout: 0 }, - vec![(id1, u128::max_value())] + vec![(id0, u128::max_value())] ), ] ); @@ -1638,7 +1794,7 @@ mod tests { output: 0, }, ], - etching: None, + ..Default::default() } .encipher(), ), @@ -1710,6 +1866,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1805,6 +1962,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1830,6 +1988,7 @@ mod tests { rune: Rune(RUNE + 1), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1876,17 +2035,17 @@ mod tests { [ ( OutPoint { - txid: txid0, + txid: txid1, vout: 0 }, - vec![(id0, u128::max_value())] + vec![(id1, u128::max_value())] ), ( OutPoint { - txid: txid1, + txid: txid0, vout: 0 }, - vec![(id1, u128::max_value())] + vec![(id0, u128::max_value())] ), ] ); @@ -1914,6 +2073,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -1969,7 +2129,7 @@ mod tests { output: 0, }, ], - etching: None, + ..Default::default() } .encipher(), ), @@ -2027,6 +2187,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -2080,6 +2241,7 @@ mod tests { rune: Rune(RUNE + 1), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -2126,17 +2288,17 @@ mod tests { [ ( OutPoint { - txid: txid0, + txid: txid1, vout: 0 }, - vec![(id0, u128::max_value())] + vec![(id1, u128::max_value())] ), ( OutPoint { - txid: txid1, + txid: txid0, vout: 0 }, - vec![(id1, u128::max_value())] + vec![(id0, u128::max_value())] ), ] ); @@ -2157,7 +2319,7 @@ mod tests { output: 0, }, ], - etching: None, + ..Default::default() } .encipher(), ), @@ -2199,17 +2361,17 @@ mod tests { [ ( OutPoint { - txid: txid2, + txid: txid1, vout: 0 }, - vec![(id0, u128::max_value())] + vec![(id1, u128::max_value())] ), ( OutPoint { - txid: txid1, + txid: txid2, vout: 0 }, - vec![(id1, u128::max_value())] + vec![(id0, u128::max_value())] ), ] ); @@ -2237,6 +2399,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -2285,7 +2448,7 @@ mod tests { amount: u128::max_value(), output: 0, }], - etching: None, + ..Default::default() } .encipher(), ), @@ -2343,6 +2506,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -2400,6 +2564,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -2464,6 +2629,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -2520,6 +2686,7 @@ mod tests { rune: Rune(RUNE), symbol: Some('$'), }), + ..Default::default() } .encipher(), ), @@ -2576,6 +2743,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -2632,6 +2800,7 @@ mod tests { rune: Rune(RUNE), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -2681,7 +2850,7 @@ mod tests { amount: 0, output: 1, }], - etching: None, + ..Default::default() } .encipher(), ), diff --git a/src/runes/runestone.rs b/src/runes/runestone.rs index bb343b0c21..ba4dd2fcbd 100644 --- a/src/runes/runestone.rs +++ b/src/runes/runestone.rs @@ -1,9 +1,56 @@ use super::*; +const TAG_BODY: u128 = 0; +const TAG_DIVISIBILITY: u128 = 1; +const TAG_RUNE: u128 = 2; +const TAG_SYMBOL: u128 = 3; + +#[allow(unused)] +const TAG_BURN: u128 = 4; + #[derive(Default, Serialize, Debug, PartialEq)] pub struct Runestone { pub edicts: Vec, pub etching: Option, + pub burn: bool, +} + +struct Message { + fields: HashMap, + body: Vec, +} + +impl Message { + fn from_integers(payload: &[u128]) -> Self { + let mut body = Vec::new(); + let mut fields = HashMap::new(); + + for i in (0..payload.len()).step_by(2) { + let tag = payload[i]; + + if tag == TAG_BODY { + let mut id = 0u128; + for chunk in payload[i + 1..].chunks_exact(3) { + id = id.saturating_add(chunk[0]); + body.push(Edict { + id, + amount: chunk[1], + output: chunk[2], + }); + } + + break; + } + + let Some(&value) = payload.get(i + 1) else { + break; + }; + + fields.entry(tag).or_insert(value); + } + + Self { fields, body } + } } impl Runestone { @@ -25,68 +72,64 @@ impl Runestone { i += length; } - let mut edicts = Vec::new(); - let mut etching = None; - let mut id = 0u128; - for chunk in integers.chunks(3) { - match *chunk { - [id_delta, amount, output] => { - id = id.saturating_add(id_delta); - edicts.push(Edict { id, amount, output }); - } - [rune] => { - etching = Some(Etching { - divisibility: 0, - rune: Rune(rune), - symbol: None, - }) - } - [rune, parameters] => { - etching = Some(Etching { - divisibility: u8::try_from(parameters & 0b11_1111) - .unwrap() - .min(MAX_DIVISIBILITY), - rune: Rune(rune), - symbol: { - let symbol = u32::try_from(parameters >> 6 & 0xFFFFFFFF).unwrap(); - if symbol > 0 { - char::from_u32(symbol) - } else { - None - } - }, - }) - } - _ => unreachable!(), - } - } + let Message { mut fields, body } = Message::from_integers(&integers); + + let etching = fields.remove(&TAG_RUNE).map(|rune| Etching { + rune: Rune(rune), + divisibility: fields + .remove(&TAG_DIVISIBILITY) + .and_then(|divisibility| u8::try_from(divisibility).ok()) + .and_then(|divisibility| (divisibility <= MAX_DIVISIBILITY).then_some(divisibility)) + .unwrap_or_default(), + symbol: fields + .remove(&TAG_SYMBOL) + .and_then(|symbol| u32::try_from(symbol).ok()) + .and_then(char::from_u32), + }); - Ok(Some(Self { edicts, etching })) + Ok(Some(Self { + edicts: body, + etching, + burn: fields.keys().any(|tag| tag % 2 == 0), + })) } #[cfg(test)] pub(crate) fn encipher(&self) -> ScriptBuf { let mut payload = Vec::new(); - let mut edicts = self.edicts.clone(); - edicts.sort_by_key(|edict| edict.id); + if let Some(etching) = self.etching { + varint::encode_to_vec(TAG_RUNE, &mut payload); + varint::encode_to_vec(etching.rune.0, &mut payload); + + if etching.divisibility != 0 && etching.divisibility <= MAX_DIVISIBILITY { + varint::encode_to_vec(TAG_DIVISIBILITY, &mut payload); + varint::encode_to_vec(etching.divisibility.into(), &mut payload); + } - 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(symbol) = etching.symbol { + varint::encode_to_vec(TAG_SYMBOL, &mut payload); + varint::encode_to_vec(symbol.into(), &mut payload); + } } - if let Some(etching) = self.etching { - varint::encode_to_vec(etching.rune.0, &mut payload); + if self.burn { + varint::encode_to_vec(TAG_BURN, &mut payload); + varint::encode_to_vec(0, &mut payload); + } - let parameters = - u128::from(etching.symbol.unwrap_or_default()) << 6 | u128::from(etching.divisibility); + if !self.edicts.is_empty() { + varint::encode_to_vec(TAG_BODY, &mut payload); - if parameters != 0 { - varint::encode_to_vec(parameters, &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; } } @@ -326,10 +369,7 @@ mod tests { lock_time: locktime::absolute::LockTime::ZERO, version: 0, }), - Ok(Some(Runestone { - edicts: Vec::new(), - etching: None, - })) + Ok(Some(Runestone::default())) ); } @@ -345,7 +385,7 @@ mod tests { #[test] fn error_in_input_aborts_search_for_runestone() { - let payload = payload(&[1, 2, 3]); + let payload = payload(&[0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -382,7 +422,7 @@ mod tests { #[test] fn deciphering_non_empty_runestone_is_successful() { - let payload = payload(&[1, 2, 3]); + let payload = payload(&[0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -406,14 +446,14 @@ mod tests { amount: 2, output: 3, }], - etching: None, + ..Default::default() })) ); } #[test] - fn additional_integer_is_rune() { - let payload = payload(&[1, 2, 3, 4]); + fn decipher_etching() { + let payload = payload(&[2, 4, 0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -442,13 +482,14 @@ mod tests { divisibility: 0, symbol: None, }), + ..Default::default() })) ); } #[test] - fn additional_two_integers_are_rune_and_divisibility() { - let payload = payload(&[1, 2, 3, 4, 5]); + fn duplicate_tags_are_ignored() { + let payload = payload(&[2, 4, 2, 5, 0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -474,16 +515,84 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: 5, + divisibility: 0, + symbol: None, + }), + ..Default::default() + })) + ); + } + + #[test] + fn tag_with_no_value_is_ignored() { + let payload = payload(&[2, 4, 2]); + + 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 { + etching: Some(Etching { + rune: Rune(4), + divisibility: 0, + symbol: None, + }), + ..Default::default() + })) + ); + } + + #[test] + fn additional_integers_in_body_are_ignored() { + let payload = payload(&[2, 4, 0, 1, 2, 3, 4, 5]); + + 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, + }], + etching: Some(Etching { + rune: Rune(4), + divisibility: 0, symbol: None, }), + ..Default::default() })) ); } #[test] - fn divisibility_above_max_is_clamped() { - let payload = payload(&[1, 2, 3, 4, (MAX_DIVISIBILITY + 1).into()]); + fn decipher_etching_with_divisibility() { + let payload = payload(&[2, 4, 1, 5, 0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -509,16 +618,17 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: MAX_DIVISIBILITY, + divisibility: 5, symbol: None, }), + ..Default::default() })) ); } #[test] - fn divisibility_is_taken_from_bits_five_to_zero() { - let payload = payload(&[1, 2, 3, 4, 0b110_0000]); + fn divisibility_above_max_is_ignored() { + let payload = payload(&[2, 4, 1, (MAX_DIVISIBILITY + 1).into(), 0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -544,16 +654,17 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: 0b10_0000, - symbol: Some(1.into()), + divisibility: 0, + symbol: None, }), + ..Default::default() })) ); } #[test] - fn symbol_is_taken_from_bits_thirty_seven_to_six() { - let payload = payload(&[1, 2, 3, 4, u128::from('a') << 6]); + fn decipher_etching_with_symbol() { + let payload = payload(&[2, 4, 3, 'a'.into(), 0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -582,13 +693,86 @@ mod tests { divisibility: 0, symbol: Some('a'), }), + ..Default::default() + })) + ); + } + + #[test] + fn decipher_etching_with_divisibility_and_symbol() { + let payload = payload(&[2, 4, 1, 1, 3, 'a'.into(), 0, 1, 2, 3]); + + 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, + }], + etching: Some(Etching { + rune: Rune(4), + divisibility: 1, + symbol: Some('a'), + }), + ..Default::default() + })) + ); + } + + #[test] + fn tag_values_are_not_parsed_as_tags() { + let payload = payload(&[2, 4, 1, 0, 0, 1, 2, 3]); + + 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, + }], + etching: Some(Etching { + rune: Rune(4), + divisibility: 0, + symbol: None, + }), + ..Default::default() })) ); } #[test] fn runestone_may_contain_multiple_edicts() { - let payload = payload(&[1, 2, 3, 3, 5, 6]); + let payload = payload(&[0, 1, 2, 3, 3, 5, 6]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -619,14 +803,14 @@ mod tests { output: 6, }, ], - etching: None, + ..Default::default() })) ); } #[test] fn id_deltas_saturate_to_max() { - let payload = payload(&[1, 2, 3, u128::max_value(), 5, 6]); + let payload = payload(&[0, 1, 2, 3, u128::max_value(), 5, 6]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -657,7 +841,7 @@ mod tests { output: 6, }, ], - etching: None, + ..Default::default() })) ); } @@ -671,11 +855,14 @@ mod tests { script_pubkey: script::Builder::new() .push_opcode(opcodes::all::OP_RETURN) .push_slice(b"RUNE_TEST") - .push_slice::<&PushBytes>(varint::encode(1).as_slice().try_into().unwrap()) .push_slice::<&PushBytes>(varint::encode(2).as_slice().try_into().unwrap()) - .push_slice::<&PushBytes>(varint::encode(3).as_slice().try_into().unwrap()) .push_slice::<&PushBytes>(varint::encode(4).as_slice().try_into().unwrap()) + .push_slice::<&PushBytes>(varint::encode(1).as_slice().try_into().unwrap()) .push_slice::<&PushBytes>(varint::encode(5).as_slice().try_into().unwrap()) + .push_slice::<&PushBytes>(varint::encode(0).as_slice().try_into().unwrap()) + .push_slice::<&PushBytes>(varint::encode(1).as_slice().try_into().unwrap()) + .push_slice::<&PushBytes>(varint::encode(2).as_slice().try_into().unwrap()) + .push_slice::<&PushBytes>(varint::encode(3).as_slice().try_into().unwrap()) .into_script(), value: 0 }], @@ -692,14 +879,15 @@ mod tests { rune: Rune(4), divisibility: 5, symbol: None, - }) + }), + ..Default::default() })) ); } #[test] fn runestone_may_be_in_second_output() { - let payload = payload(&[1, 2, 3]); + let payload = payload(&[0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -728,15 +916,15 @@ mod tests { id: 1, amount: 2, output: 3, - },], - etching: None, + }], + ..Default::default() })) ); } #[test] fn runestone_may_be_after_non_matching_op_return() { - let payload = payload(&[1, 2, 3]); + let payload = payload(&[0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -768,8 +956,8 @@ mod tests { id: 1, amount: 2, output: 3, - },], - etching: None, + }], + ..Default::default() })) ); } @@ -779,7 +967,15 @@ mod tests { #[track_caller] fn case(edicts: Vec, etching: Option, size: usize) { assert_eq!( - Runestone { edicts, etching }.encipher().len() - 1 - b"RUNE_TEST".len(), + Runestone { + edicts, + etching, + ..Default::default() + } + .encipher() + .len() + - 1 + - b"RUNE_TEST".len(), size ); } @@ -793,7 +989,7 @@ mod tests { rune: Rune(0), symbol: None, }), - 3, + 4, ); case( @@ -803,7 +999,7 @@ mod tests { rune: Rune(0), symbol: None, }), - 4, + 6, ); case( @@ -813,7 +1009,7 @@ mod tests { rune: Rune(0), symbol: Some('$'), }), - 5, + 8, ); case( @@ -823,7 +1019,7 @@ mod tests { rune: Rune(u128::max_value()), symbol: None, }), - 21, + 22, ); case( @@ -841,7 +1037,7 @@ mod tests { rune: Rune(u128::max_value()), symbol: None, }), - 25, + 28, ); case( @@ -859,7 +1055,7 @@ mod tests { rune: Rune(u128::max_value()), symbol: None, }), - 43, + 46, ); case( @@ -873,7 +1069,7 @@ mod tests { output: 0, }], None, - 28, + 29, ); case( @@ -898,7 +1094,7 @@ mod tests { }, ], None, - 49, + 50, ); case( @@ -932,7 +1128,7 @@ mod tests { }, ], None, - 70, + 71, ); case( @@ -949,7 +1145,7 @@ mod tests { 4 ], None, - 55, + 56, ); case( @@ -966,7 +1162,7 @@ mod tests { 5 ], None, - 67, + 68, ); case( @@ -983,7 +1179,7 @@ mod tests { 5 ], None, - 64, + 65, ); case( @@ -1000,7 +1196,7 @@ mod tests { 5 ], None, - 62, + 63, ); } } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 0821998241..1306aa7152 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -3153,6 +3153,7 @@ mod tests { rune: Rune(u128::from(21_000_000 * COIN_VALUE)), symbol: None, }), + ..Default::default() } .encipher(), ), @@ -3221,6 +3222,7 @@ mod tests { rune, symbol: Some('$'), }), + ..Default::default() } .encipher(), ), @@ -3357,6 +3359,7 @@ mod tests { rune, symbol: None, }), + ..Default::default() } .encipher(), ), From 49ec31c5ffe0043bac765c25d22fd7d144830999 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 17 Oct 2023 11:32:26 -0700 Subject: [PATCH 2/7] Implement Default for RuneEntry --- src/index/entry.rs | 13 ++ src/runes.rs | 330 +++++++++++---------------------------- src/runes/runestone.rs | 60 ++++--- src/subcommand/server.rs | 17 +- 4 files changed, 150 insertions(+), 270 deletions(-) diff --git a/src/index/entry.rs b/src/index/entry.rs index abc63de3fd..3ec8afff72 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -32,6 +32,19 @@ pub(crate) struct RuneEntry { pub(crate) symbol: Option, } +impl Default for RuneEntry { + fn default() -> Self { + Self { + burned: 0, + divisibility: 0, + etching: Txid::all_zeros(), + rune: Rune(0), + supply: 0, + symbol: None, + } + } +} + pub(super) type RuneEntryValue = (u128, u8, (u128, u128), u128, u128, u32); impl Entry for RuneEntry { diff --git a/src/runes.rs b/src/runes.rs index f4fa16f33f..4fbae8ce6a 100644 --- a/src/runes.rs +++ b/src/runes.rs @@ -83,9 +83,8 @@ mod tests { op_return: Some( Runestone { etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -106,12 +105,9 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), - supply: 0, - symbol: None, + ..Default::default() } )] ); @@ -137,9 +133,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -160,12 +155,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -195,9 +188,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(u128::from(Sat::SUPPLY - 150 * COIN_VALUE - 1)), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -230,9 +222,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(u128::from(Sat::SUPPLY - 150 * COIN_VALUE)), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -253,12 +244,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(u128::from(Sat::SUPPLY - 150 * COIN_VALUE)), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -290,7 +279,7 @@ mod tests { etching: Some(Etching { divisibility: 1, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -311,12 +300,11 @@ mod tests { [( id, RuneEntry { - burned: 0, rune: Rune(RUNE), etching: txid, divisibility: 1, supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -352,9 +340,8 @@ mod tests { }, ], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -375,12 +362,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -416,9 +401,8 @@ mod tests { }, ], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -439,12 +423,11 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), symbol: None, + ..Default::default() } )] ); @@ -473,9 +456,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -496,12 +478,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: 100, - symbol: None, + ..Default::default() } )] ); @@ -537,9 +517,8 @@ mod tests { }, ], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -561,11 +540,10 @@ mod tests { id, RuneEntry { burned: 100, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: 200, - symbol: None, + ..Default::default() } )] ); @@ -604,9 +582,8 @@ mod tests { }, ], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -627,12 +604,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: 100, - symbol: None, + ..Default::default() } )] ); @@ -661,9 +636,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -684,12 +658,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -728,12 +700,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -768,9 +738,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), burn: true, } @@ -791,12 +760,9 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), - supply: 0, - symbol: None, + ..Default::default() } )] ); @@ -822,9 +788,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -845,12 +810,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -886,11 +849,10 @@ mod tests { id, RuneEntry { burned: u128::max_value(), - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -916,9 +878,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -939,12 +900,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -973,12 +932,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1014,9 +971,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1037,12 +993,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1071,12 +1025,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1111,9 +1063,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1134,12 +1085,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1159,9 +1108,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1177,12 +1125,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1211,9 +1157,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1234,12 +1179,10 @@ mod tests { [( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1265,9 +1208,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE + 1), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1289,23 +1231,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -1344,23 +1282,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -1396,9 +1330,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1419,12 +1352,10 @@ mod tests { [( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1450,9 +1381,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE + 1), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1474,23 +1404,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -1529,23 +1455,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -1594,23 +1516,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -1658,9 +1576,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1681,12 +1598,10 @@ mod tests { [( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1712,9 +1627,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE + 1), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1736,23 +1650,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -1809,23 +1719,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -1862,9 +1768,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1885,12 +1790,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1924,12 +1827,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -1958,9 +1859,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -1984,9 +1884,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE + 1), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2008,23 +1907,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ] @@ -2069,9 +1964,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2092,12 +1986,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -2143,12 +2035,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -2183,9 +2073,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2206,12 +2095,10 @@ mod tests { [( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -2237,9 +2124,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE + 1), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2261,23 +2147,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -2334,23 +2216,19 @@ mod tests { ( id0, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } ), ( id1, RuneEntry { - burned: 0, - divisibility: 0, etching: txid1, rune: Rune(RUNE + 1), supply: u128::max_value(), - symbol: None, + ..Default::default() } ) ] @@ -2395,9 +2273,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2418,12 +2295,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value() / 2, - symbol: None, + ..Default::default() } )] ); @@ -2462,12 +2337,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value() / 2, - symbol: None, + ..Default::default() } )] ); @@ -2502,9 +2375,8 @@ mod tests { output: 1, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2526,11 +2398,10 @@ mod tests { id, RuneEntry { burned: u128::max_value(), - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -2560,9 +2431,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2583,12 +2453,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -2625,9 +2493,8 @@ mod tests { }, ], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2648,12 +2515,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -2682,9 +2547,9 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), symbol: Some('$'), + ..Default::default() }), ..Default::default() } @@ -2705,12 +2570,11 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), symbol: Some('$'), + ..Default::default() } )] ); @@ -2739,9 +2603,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2762,12 +2625,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -2796,9 +2657,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(RUNE), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -2819,12 +2679,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -2864,12 +2722,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid0, rune: Rune(RUNE), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); diff --git a/src/runes/runestone.rs b/src/runes/runestone.rs index ba4dd2fcbd..93366e3edf 100644 --- a/src/runes/runestone.rs +++ b/src/runes/runestone.rs @@ -479,8 +479,7 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: 0, - symbol: None, + ..Default::default() }), ..Default::default() })) @@ -515,8 +514,7 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: 0, - symbol: None, + ..Default::default() }), ..Default::default() })) @@ -546,8 +544,7 @@ mod tests { Ok(Some(Runestone { etching: Some(Etching { rune: Rune(4), - divisibility: 0, - symbol: None, + ..Default::default() }), ..Default::default() })) @@ -582,8 +579,7 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: 0, - symbol: None, + ..Default::default() }), ..Default::default() })) @@ -619,7 +615,7 @@ mod tests { etching: Some(Etching { rune: Rune(4), divisibility: 5, - symbol: None, + ..Default::default() }), ..Default::default() })) @@ -654,8 +650,7 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: 0, - symbol: None, + ..Default::default() }), ..Default::default() })) @@ -690,8 +685,8 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: 0, symbol: Some('a'), + ..Default::default() }), ..Default::default() })) @@ -762,8 +757,7 @@ mod tests { }], etching: Some(Etching { rune: Rune(4), - divisibility: 0, - symbol: None, + ..Default::default() }), ..Default::default() })) @@ -878,7 +872,7 @@ mod tests { etching: Some(Etching { rune: Rune(4), divisibility: 5, - symbol: None, + ..Default::default() }), ..Default::default() })) @@ -985,9 +979,8 @@ mod tests { case( Vec::new(), Some(Etching { - divisibility: 0, rune: Rune(0), - symbol: None, + ..Default::default() }), 4, ); @@ -997,7 +990,7 @@ mod tests { Some(Etching { divisibility: MAX_DIVISIBILITY, rune: Rune(0), - symbol: None, + ..Default::default() }), 6, ); @@ -1015,9 +1008,8 @@ mod tests { case( Vec::new(), Some(Etching { - divisibility: 0, rune: Rune(u128::max_value()), - symbol: None, + ..Default::default() }), 22, ); @@ -1035,7 +1027,7 @@ mod tests { Some(Etching { divisibility: MAX_DIVISIBILITY, rune: Rune(u128::max_value()), - symbol: None, + ..Default::default() }), 28, ); @@ -1053,11 +1045,35 @@ mod tests { Some(Etching { divisibility: MAX_DIVISIBILITY, rune: Rune(u128::max_value()), - symbol: None, + ..Default::default() }), 46, ); + case( + vec![Edict { + amount: 0, + id: RuneId { + height: 1_000_000, + index: u16::max_value(), + } + .into(), + output: 0, + }], + None, + 11, + ); + + case( + vec![Edict { + amount: 0, + id: 1 << 48, + output: 0, + }], + None, + 12, + ); + case( vec![Edict { amount: u128::max_value(), diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 1306aa7152..97da562c7f 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -3149,9 +3149,8 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune: Rune(u128::from(21_000_000 * COIN_VALUE)), - symbol: None, + ..Default::default() }), ..Default::default() } @@ -3172,12 +3171,10 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune: Rune(u128::from(21_000_000 * COIN_VALUE)), supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); @@ -3218,9 +3215,9 @@ mod tests { output: 0, }], etching: Some(Etching { - divisibility: 0, rune, symbol: Some('$'), + ..Default::default() }), ..Default::default() } @@ -3241,12 +3238,11 @@ mod tests { [( id, RuneEntry { - burned: 0, - divisibility: 0, etching: txid, rune, supply: u128::max_value(), symbol: Some('$'), + ..Default::default() } )] ); @@ -3357,7 +3353,7 @@ mod tests { etching: Some(Etching { divisibility: 1, rune, - symbol: None, + ..Default::default() }), ..Default::default() } @@ -3378,12 +3374,11 @@ mod tests { [( id, RuneEntry { - burned: 0, divisibility: 1, etching: txid, rune, supply: u128::max_value(), - symbol: None, + ..Default::default() } )] ); From 1da674dc9c5cc87fbdb2c3d0e094f0fe60051c74 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 17 Oct 2023 12:21:58 -0700 Subject: [PATCH 3/7] Fix name --- src/runes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runes.rs b/src/runes.rs index 4fbae8ce6a..062b59e7db 100644 --- a/src/runes.rs +++ b/src/runes.rs @@ -721,7 +721,7 @@ mod tests { } #[test] - fn etching_rune_is_burned_if_an_unrecognized_even_tag_is_encountered() { + fn etched_rune_is_burned_if_an_unrecognized_even_tag_is_encountered() { let context = Context::builder() .arg("--index-runes-pre-alpha-i-agree-to-get-rekt") .build(); From 0f20b2ceb423c5ebc4a4e5c222340ebde4dec007 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 17 Oct 2023 12:49:27 -0700 Subject: [PATCH 4/7] Add new tests --- src/runes/runestone.rs | 71 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/runes/runestone.rs b/src/runes/runestone.rs index 93366e3edf..dd96c1ada0 100644 --- a/src/runes/runestone.rs +++ b/src/runes/runestone.rs @@ -38,7 +38,6 @@ impl Message { output: chunk[2], }); } - break; } @@ -521,6 +520,41 @@ mod tests { ); } + #[test] + fn unrecognized_even_tag_is_ignored() { + let payload = payload(&[126, 0, 1, 2, 3]); + + 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, + }], + etching: Some(Etching { + rune: Rune(4), + ..Default::default() + }), + ..Default::default() + })) + ); + } + #[test] fn tag_with_no_value_is_ignored() { let payload = payload(&[2, 4, 2]); @@ -657,6 +691,41 @@ mod tests { ); } + #[test] + fn symbol_above_max_is_ignored() { + let payload = payload(&[2, 4, 3, u32::from(char::MAX) + 1, 0, 1, 2, 3]); + + 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, + }], + etching: Some(Etching { + rune: Rune(4), + ..Default::default() + }), + ..Default::default() + })) + ); + } + #[test] fn decipher_etching_with_symbol() { let payload = payload(&[2, 4, 3, 'a'.into(), 0, 1, 2, 3]); From 65333bf7a9da8e12b546c4f44add8c5fd0f7e465 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 17 Oct 2023 12:51:11 -0700 Subject: [PATCH 5/7] Fix --- src/runes/runestone.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runes/runestone.rs b/src/runes/runestone.rs index dd96c1ada0..b133803305 100644 --- a/src/runes/runestone.rs +++ b/src/runes/runestone.rs @@ -522,7 +522,7 @@ mod tests { #[test] fn unrecognized_even_tag_is_ignored() { - let payload = payload(&[126, 0, 1, 2, 3]); + let payload = payload(&[127, 100, 0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); @@ -693,7 +693,7 @@ mod tests { #[test] fn symbol_above_max_is_ignored() { - let payload = payload(&[2, 4, 3, u32::from(char::MAX) + 1, 0, 1, 2, 3]); + let payload = payload(&[2, 4, 3, u128::from(u32::from(char::MAX) + 1), 0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); From ebda3dfcf9d1aea719e3e21c753a30173db9a443 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 17 Oct 2023 12:52:37 -0700 Subject: [PATCH 6/7] Fix name --- src/runes/runestone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runes/runestone.rs b/src/runes/runestone.rs index b133803305..4d4b1fb4f7 100644 --- a/src/runes/runestone.rs +++ b/src/runes/runestone.rs @@ -521,7 +521,7 @@ mod tests { } #[test] - fn unrecognized_even_tag_is_ignored() { + fn unrecognized_odd_tag_is_ignored() { let payload = payload(&[127, 100, 0, 1, 2, 3]); let payload: &PushBytes = payload.as_slice().try_into().unwrap(); From dfda6fdd7c4c82f911b87f07df448c18c33ac10e Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 17 Oct 2023 12:58:26 -0700 Subject: [PATCH 7/7] Fix --- src/runes/runestone.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/runes/runestone.rs b/src/runes/runestone.rs index 4d4b1fb4f7..ad8cd17a3f 100644 --- a/src/runes/runestone.rs +++ b/src/runes/runestone.rs @@ -546,10 +546,6 @@ mod tests { amount: 2, output: 3, }], - etching: Some(Etching { - rune: Rune(4), - ..Default::default() - }), ..Default::default() })) );