From 20d32068b0275a39c28cedbb98475f88b88b9d14 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:07:34 -0800 Subject: [PATCH 01/22] Use distinct inscription IDs --- .../server => }/deserialize_from_str.rs | 2 +- src/index.rs | 190 +++++++----------- src/index/array.rs | 57 ++++++ src/index/updater.rs | 16 +- src/index/updater/inscription_updater.rs | 10 +- src/inscription_id.rs | 123 ++++++++++++ src/main.rs | 8 +- src/subcommand/server.rs | 12 +- src/subcommand/wallet/inscribe.rs | 4 +- src/templates/home.rs | 2 +- src/templates/iframe.rs | 4 +- src/templates/inscription.rs | 10 +- src/templates/inscriptions.rs | 2 +- src/templates/transaction.rs | 4 +- src/test.rs | 10 + templates/transaction.html | 4 +- 16 files changed, 307 insertions(+), 151 deletions(-) rename src/{subcommand/server => }/deserialize_from_str.rs (91%) create mode 100644 src/index/array.rs create mode 100644 src/inscription_id.rs diff --git a/src/subcommand/server/deserialize_from_str.rs b/src/deserialize_from_str.rs similarity index 91% rename from src/subcommand/server/deserialize_from_str.rs rename to src/deserialize_from_str.rs index 3f7e0ea98e..f4c537f546 100644 --- a/src/subcommand/server/deserialize_from_str.rs +++ b/src/deserialize_from_str.rs @@ -11,7 +11,7 @@ where D: Deserializer<'de>, { Ok(Self( - FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom)?, + FromStr::from_str(&String::deserialize(deserializer)?).map_err(serde::de::Error::custom)?, )) } } diff --git a/src/index.rs b/src/index.rs index 0ff256bb77..44e5bc0019 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1,5 +1,5 @@ use { - self::updater::Updater, + self::{array::Array, updater::Updater}, super::*, bitcoin::BlockHeader, bitcoincore_rpc::{json::GetBlockHeaderResult, Auth, Client}, @@ -10,11 +10,12 @@ use { std::sync::atomic::{self, AtomicBool}, }; +mod array; mod rtx; mod updater; type BlockHashArray = [u8; 32]; -type InscriptionIdArray = [u8; 32]; +type InscriptionIdArray = [u8; 36]; type OutPointArray = [u8; 36]; type SatPointArray = [u8; 44]; type SatRangeArray = [u8; 11]; @@ -41,34 +42,6 @@ define_table! { SAT_TO_SATPOINT, u64, &SatPointArray } define_table! { STATISTIC_TO_COUNT, u64, u64 } define_table! { WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP, u64, u128 } -fn encode_outpoint(outpoint: OutPoint) -> OutPointArray { - let mut array = [0; 36]; - outpoint - .consensus_encode(&mut array.as_mut_slice()) - .unwrap(); - array -} - -fn encode_satpoint(satpoint: SatPoint) -> SatPointArray { - let mut array = [0; 44]; - satpoint - .consensus_encode(&mut array.as_mut_slice()) - .unwrap(); - array -} - -fn decode_satpoint(array: SatPointArray) -> SatPoint { - Decodable::consensus_decode(&mut io::Cursor::new(array)).unwrap() -} - -fn decode_outpoint(array: OutPointArray) -> OutPoint { - Decodable::consensus_decode(&mut io::Cursor::new(array)).unwrap() -} - -fn decode_inscription_id(array: InscriptionIdArray) -> InscriptionId { - Decodable::consensus_decode(&mut io::Cursor::new(array)).unwrap() -} - pub(crate) struct Index { auth: Auth, client: Client, @@ -440,7 +413,7 @@ impl Index { let sat_to_satpoint = rtx.open_table(SAT_TO_SATPOINT)?; for (sat, satpoint) in sat_to_satpoint.range(0..)? { - result.push((Sat(sat.value()), decode_satpoint(*satpoint.value()))); + result.push((Sat(sat.value()), Array::from_array(*satpoint.value()))); } Ok(Some(result)) @@ -457,7 +430,7 @@ impl Index { .begin_read()? .open_table(SAT_TO_SATPOINT)? .get(&sat.n())? - .map(|satpoint| decode_satpoint(*satpoint.value())), + .map(|satpoint| Array::from_array(*satpoint.value())), ) } else { Ok(None) @@ -494,7 +467,7 @@ impl Index { .begin_read()? .open_table(SAT_TO_INSCRIPTION_ID)? .get(&sat.n())? - .map(|inscription_id| decode_inscription_id(*inscription_id.value())), + .map(|inscription_id| Array::from_array(*inscription_id.value())), ) } @@ -507,7 +480,7 @@ impl Index { .database .begin_read()? .open_table(INSCRIPTION_ID_TO_INSCRIPTION_NUMBER)? - .get(&id.as_inner())? + .get(&id.array())? .map(|n| n.value()), ) } @@ -522,7 +495,7 @@ impl Index { .begin_read()? .open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)? .get(&n)? - .map(|id| decode_inscription_id(*id.value())), + .map(|id| Array::from_array(*id.value())), ) } @@ -535,7 +508,7 @@ impl Index { .database .begin_read()? .open_table(INSCRIPTION_ID_TO_SAT)? - .get(inscription_id.as_inner())? + .get(&inscription_id.array())? .map(|value| Sat(value.value())), ) } @@ -548,13 +521,13 @@ impl Index { .database .begin_read()? .open_table(INSCRIPTION_ID_TO_SATPOINT)? - .get(inscription_id.as_inner())? - .map(|satpoint| decode_satpoint(*satpoint.value())) + .get(&inscription_id.array())? + .map(|satpoint| Array::from_array(*satpoint.value())) else { return Ok(None); }; - let Some(inscription) = self.get_transaction(inscription_id)?.and_then(|tx| Inscription::from_transaction(&tx)) else { + let Some(inscription) = self.get_transaction(inscription_id.txid)?.and_then(|tx| Inscription::from_transaction(&tx)) else { return Ok(None); }; @@ -617,9 +590,8 @@ impl Index { for chunk in value.value().chunks_exact(11) { let (start, end) = Index::decode_sat_range(chunk.try_into().unwrap()); if start <= sat && sat < end { - let outpoint = decode_outpoint(*key.value()); return Ok(Some(SatPoint { - outpoint, + outpoint: Array::from_array(*key.value()), offset: offset + sat - start, })); } @@ -644,9 +616,9 @@ impl Index { pub(crate) fn list(&self, outpoint: OutPoint) -> Result> { self.require_sat_index("list")?; - let outpoint_encoded = encode_outpoint(outpoint); + let array = outpoint.array(); - let sat_ranges = self.list_inner(outpoint_encoded)?; + let sat_ranges = self.list_inner(array)?; match sat_ranges { Some(sat_ranges) => Ok(Some(List::Unspent( @@ -705,8 +677,8 @@ impl Index { .range([0; 44]..)? .map(|(satpoint, id)| { ( - decode_satpoint(*satpoint.value()), - decode_inscription_id(*id.value()), + Array::from_array(*satpoint.value()), + Array::from_array(*id.value()), ) }) .take(n.unwrap_or(usize::MAX)) @@ -723,7 +695,7 @@ impl Index { .iter()? .rev() .take(n) - .map(|(_number, id)| decode_inscription_id(*id.value())) + .map(|(_number, id)| Array::from_array(*id.value())) .collect(), ) } @@ -733,7 +705,7 @@ impl Index { .database .begin_read()? .open_table(INSCRIPTION_ID_TO_HEIGHT)? - .get(inscription_id.as_inner())? + .get(&inscription_id.array())? .map(|x| x.value()) .ok_or_else(|| anyhow!("no height for inscription")) } @@ -757,9 +729,9 @@ impl Index { ); assert_eq!( - decode_satpoint( + SatPoint::from_array( *inscription_id_to_satpoint - .get(&inscription_id.as_inner()) + .get(&inscription_id.array()) .unwrap() .unwrap() .value() @@ -768,9 +740,9 @@ impl Index { ); assert_eq!( - InscriptionId::from_inner( + InscriptionId::from_array( *satpoint_to_inscription_id - .get(&encode_satpoint(satpoint)) + .get(&satpoint.array()) .unwrap() .unwrap() .value() @@ -780,7 +752,7 @@ impl Index { if self.has_sat_index().unwrap() { assert_eq!( - InscriptionId::from_inner( + InscriptionId::from_array( *rtx .open_table(SAT_TO_INSCRIPTION_ID) .unwrap() @@ -793,7 +765,7 @@ impl Index { ); assert_eq!( - decode_satpoint( + SatPoint::from_array( *rtx .open_table(SAT_TO_SATPOINT) .unwrap() @@ -811,20 +783,22 @@ impl Index { satpoint_to_id: &'a impl ReadableTable<&'tx SatPointArray, &'tx InscriptionIdArray>, outpoint: OutPoint, ) -> Result + 'tx> { - let start = encode_satpoint(SatPoint { + let start = SatPoint { outpoint, offset: 0, - }); + } + .array(); - let end = encode_satpoint(SatPoint { + let end = SatPoint { outpoint, offset: u64::MAX, - }); + } + .array(); Ok(satpoint_to_id.range(start..=end)?.map(|(satpoint, id)| { ( - decode_satpoint(*satpoint.value()), - InscriptionId::from_inner(*id.value()), + Array::from_array(*satpoint.value()), + Array::from_array(*id.value()), ) })) } @@ -960,7 +934,8 @@ mod tests { { let context = Context::builder().build(); context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(template.clone()); + let txid = context.rpc_server.broadcast_tx(template.clone()); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); assert_eq!( @@ -968,10 +943,7 @@ mod tests { Some(( inscription, SatPoint { - outpoint: OutPoint { - txid: inscription_id, - vout: 0, - }, + outpoint: OutPoint { txid, vout: 0 }, offset: 0, } )) @@ -983,7 +955,8 @@ mod tests { .arg("--first-inscription-height=3") .build(); context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(template); + let txid = context.rpc_server.broadcast_tx(template); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); assert_eq!( @@ -1290,21 +1263,19 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); context.index.assert_inscription_location( inscription_id, SatPoint { - outpoint: OutPoint { - txid: inscription_id, - vout: 0, - }, + outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, 50 * COIN_VALUE, @@ -1317,21 +1288,19 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); context.index.assert_inscription_location( inscription_id, SatPoint { - outpoint: OutPoint { - txid: inscription_id, - vout: 0, - }, + outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, 50 * COIN_VALUE, @@ -1363,17 +1332,20 @@ mod tests { for context in Context::configurations() { context.mine_blocks(2); - let first_inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let first_txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); - let second_inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let first_inscription_id = InscriptionId::from(first_txid); + + let second_txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 0, 0)], witness: inscription("text/png", [1; 100]).to_witness(), ..Default::default() }); + let second_inscription_id = InscriptionId::from(second_txid); context.mine_blocks(1); @@ -1415,21 +1387,19 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); context.index.assert_inscription_location( inscription_id, SatPoint { - outpoint: OutPoint { - txid: inscription_id, - vout: 0, - }, + outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, 50 * COIN_VALUE, @@ -1466,21 +1436,19 @@ mod tests { let context = Context::builder().args(args).build(); context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); context.index.assert_inscription_location( inscription_id, SatPoint { - outpoint: OutPoint { - txid: inscription_id, - vout: 0, - }, + outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, 50 * COIN_VALUE, @@ -1512,11 +1480,12 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); @@ -1547,12 +1516,13 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], fee: 50 * COIN_VALUE, witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); let coinbase_tx = context.mine_blocks(1)[0].txdata[0].txid(); @@ -1575,12 +1545,13 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], fee: 50 * COIN_VALUE, witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks_with_subsidy(1, 0); @@ -1600,22 +1571,24 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let first_inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let first_txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], fee: 50 * COIN_VALUE, witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let first_inscription_id = InscriptionId::from(first_txid); context.mine_blocks_with_subsidy(1, 0); context.mine_blocks(1); - let second_inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let second_txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(3, 0, 0)], fee: 50 * COIN_VALUE, witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let second_inscription_id = InscriptionId::from(second_txid); context.mine_blocks_with_subsidy(1, 0); @@ -1672,12 +1645,13 @@ mod tests { context.mine_blocks_with_subsidy(1, 0); context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 0, 0)], outputs: 2, witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); context.rpc_server.broadcast_tx(TransactionTemplate { @@ -1703,22 +1677,20 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], outputs: 2, witness: inscription("text/plain", "hello").to_witness(), output_values: &[0, 50 * COIN_VALUE], ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks(1); context.index.assert_inscription_location( inscription_id, SatPoint { - outpoint: OutPoint { - txid: inscription_id, - vout: 1, - }, + outpoint: OutPoint { txid, vout: 1 }, offset: 0, }, 50 * COIN_VALUE, @@ -1731,12 +1703,13 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], fee: 50 * COIN_VALUE, witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); context.mine_blocks_with_subsidy(1, 0); context.index.assert_inscription_location( @@ -1840,19 +1813,18 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); - let inscription_id = context.rpc_server.broadcast_tx(TransactionTemplate { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/plain", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); + assert_eq!( context .index - .get_inscriptions_on_output(OutPoint { - txid: inscription_id, - vout: 0, - }) + .get_inscriptions_on_output(OutPoint { txid, vout: 0 }) .unwrap(), [] ); @@ -1862,10 +1834,7 @@ mod tests { assert_eq!( context .index - .get_inscriptions_on_output(OutPoint { - txid: inscription_id, - vout: 0, - }) + .get_inscriptions_on_output(OutPoint { txid, vout: 0 }) .unwrap(), [inscription_id] ); @@ -1880,10 +1849,7 @@ mod tests { assert_eq!( context .index - .get_inscriptions_on_output(OutPoint { - txid: inscription_id, - vout: 0, - }) + .get_inscriptions_on_output(OutPoint { txid, vout: 0 }) .unwrap(), [] ); diff --git a/src/index/array.rs b/src/index/array.rs new file mode 100644 index 0000000000..399c7f94a9 --- /dev/null +++ b/src/index/array.rs @@ -0,0 +1,57 @@ +use super::*; + +pub(super) trait Array: Sized { + type Array; + + fn from_array(array: Self::Array) -> Self; + + fn array(self) -> Self::Array; +} + +impl Array for InscriptionId { + type Array = InscriptionIdArray; + + fn from_array(array: Self::Array) -> Self { + let (txid, vout) = array.split_at(32); + Self { + txid: Txid::from_inner(txid.try_into().unwrap()), + vout: u32::from_be_bytes(vout.try_into().unwrap()), + } + } + + fn array(self) -> Self::Array { + let mut array = [0; 36]; + let (txid, vout) = array.split_at_mut(32); + txid.copy_from_slice(self.txid.as_inner()); + vout.copy_from_slice(&self.vout.to_be_bytes()); + array + } +} + +impl Array for OutPoint { + type Array = OutPointArray; + + fn from_array(array: Self::Array) -> Self { + Decodable::consensus_decode(&mut io::Cursor::new(array)).unwrap() + } + + fn array(self) -> Self::Array { + let mut array = [0; 36]; + self.consensus_encode(&mut array.as_mut_slice()).unwrap(); + array + } +} + +impl Array for SatPoint { + type Array = SatPointArray; + + fn from_array(array: Self::Array) -> Self { + Decodable::consensus_decode(&mut io::Cursor::new(array)).unwrap() + } + + fn array(self) -> Self::Array { + let mut array = [0; 44]; + self.consensus_encode(&mut array.as_mut_slice()).unwrap(); + array + } +} diff --git a/src/index/updater.rs b/src/index/updater.rs index 9ab46982d6..cd01279e3b 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -324,7 +324,7 @@ impl Updater { let mut input_sat_ranges = VecDeque::new(); for input in &tx.input { - let key = encode_outpoint(input.previous_output); + let key = input.previous_output.array(); let sat_ranges = match self.range_cache.remove(&key) { Some(sat_ranges) => { @@ -373,10 +373,11 @@ impl Updater { if !Sat(start).is_common() { sat_to_satpoint.insert( &start, - &encode_satpoint(SatPoint { + &SatPoint { outpoint: OutPoint::null(), offset: lost_sats, - }), + } + .array(), )?; } @@ -435,10 +436,11 @@ impl Updater { if !Sat(range.0).is_common() { sat_to_satpoint.insert( &range.0, - &encode_satpoint(SatPoint { + &SatPoint { outpoint, offset: output.value - remaining, - }), + } + .array(), )?; } @@ -467,7 +469,7 @@ impl Updater { *outputs_traversed += 1; - self.range_cache.insert(encode_outpoint(outpoint), sats); + self.range_cache.insert(outpoint.array(), sats); self.outputs_inserted_since_flush += 1; } @@ -504,7 +506,7 @@ impl Updater { let mut outpoint_to_value = wtx.open_table(OUTPOINT_TO_VALUE)?; for (outpoint, value) in value_cache { - outpoint_to_value.insert(&encode_outpoint(outpoint), &value)?; + outpoint_to_value.insert(&outpoint.array(), &value)?; } } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 7bab2ae8b1..037602b2f3 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -80,7 +80,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { if Inscription::from_transaction(tx).is_some() { inscriptions.push(Flotsam { - inscription_id: txid, + inscription_id: txid.into(), offset: 0, origin: Origin::New, }); @@ -105,7 +105,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { value } else if let Some(value) = self .outpoint_to_value - .remove(&encode_outpoint(tx_in.previous_output))? + .remove(&tx_in.previous_output.array())? { value.value() } else { @@ -198,11 +198,11 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { flotsam: Flotsam, new_satpoint: SatPoint, ) -> Result { - let inscription_id = flotsam.inscription_id.into_inner(); + let inscription_id = flotsam.inscription_id.array(); match flotsam.origin { Origin::Old(old_satpoint) => { - self.satpoint_to_id.remove(&encode_satpoint(old_satpoint))?; + self.satpoint_to_id.remove(&old_satpoint.array())?; } Origin::New => { self.id_to_height.insert(&inscription_id, &self.height)?; @@ -231,7 +231,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } - let new_satpoint = encode_satpoint(new_satpoint); + let new_satpoint = new_satpoint.array(); self.satpoint_to_id.insert(&new_satpoint, &inscription_id)?; self.id_to_satpoint.insert(&inscription_id, &new_satpoint)?; diff --git a/src/inscription_id.rs b/src/inscription_id.rs new file mode 100644 index 0000000000..c5d3cbcd9b --- /dev/null +++ b/src/inscription_id.rs @@ -0,0 +1,123 @@ +use super::*; + +// TODO: +// - fix tests +// - use TXIDiINSCRIPTION_NUMBER +// - print inscription ID on send +// - make parse recognize inscription IDs +// - make sure we use inscriptionIDs in index methods + +#[derive(Debug, PartialEq, Copy, Clone)] +pub(crate) struct InscriptionId { + pub(crate) txid: Txid, + // TODO: rename to index, i, or inscription + pub(crate) vout: u32, +} + +impl<'de> Deserialize<'de> for InscriptionId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(DeserializeFromStr::deserialize(deserializer)?.0) + } +} + +impl Display for InscriptionId { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}i{}", self.txid, self.vout) + } +} + +impl FromStr for InscriptionId { + type Err = Error; + + fn from_str(s: &str) -> Result { + if !s.is_ascii() { + bail!("invalid character"); + } + + const TXID_LEN: usize = 64; + const MIN_LEN: usize = TXID_LEN + 2; + + if s.len() < MIN_LEN { + bail!("invalid length"); + } + + let txid = &s[..TXID_LEN]; + + if &s[TXID_LEN..TXID_LEN + 1] != "i" { + bail!("invalid separator"); + } + + let vout = &s[TXID_LEN + 1..]; + + Ok(Self { + txid: txid.parse()?, + vout: vout.parse()?, + }) + } +} + +impl From for InscriptionId { + fn from(txid: Txid) -> Self { + Self { txid, vout: 0 } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn display() { + assert_eq!( + inscription_id(1).to_string(), + "1111111111111111111111111111111111111111111111111111111111111111i1", + ); + assert_eq!( + InscriptionId { + txid: txid(1), + vout: 0, + } + .to_string(), + "1111111111111111111111111111111111111111111111111111111111111111i0", + ); + assert_eq!( + InscriptionId { + txid: txid(1), + vout: 0xFFFFFFFF, + } + .to_string(), + "1111111111111111111111111111111111111111111111111111111111111111i4294967295", + ); + } + + #[test] + fn from_str() { + assert_eq!( + "1111111111111111111111111111111111111111111111111111111111111111i1" + .parse::() + .unwrap(), + inscription_id(1), + ); + assert_eq!( + "1111111111111111111111111111111111111111111111111111111111111111i4294967295" + .parse::() + .unwrap(), + InscriptionId { + txid: txid(1), + vout: 0xFFFFFFFF, + }, + ); + assert_eq!( + "1111111111111111111111111111111111111111111111111111111111111111i4294967295" + .parse::() + .unwrap(), + InscriptionId { + txid: txid(1), + vout: 0xFFFFFFFF, + }, + ); + } +} diff --git a/src/main.rs b/src/main.rs index 2aa93c0230..d64721ba5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,10 +18,12 @@ use { content::Content, decimal::Decimal, degree::Degree, + deserialize_from_str::DeserializeFromStr, epoch::Epoch, height::Height, index::{Index, List}, inscription::Inscription, + inscription_id::InscriptionId, object::Object, options::Options, ordinal_address::OrdinalAddress, @@ -50,7 +52,7 @@ use { html_escaper::{Escape, Trusted}, lazy_static::lazy_static, regex::Regex, - serde::{Deserialize, Serialize}, + serde::{Deserialize, Deserializer, Serialize}, std::{ cmp, collections::{BTreeMap, HashSet, VecDeque}, @@ -88,10 +90,12 @@ mod chain; mod content; mod decimal; mod degree; +mod deserialize_from_str; mod epoch; mod height; mod index; mod inscription; +mod inscription_id; mod object; mod options; mod ordinal_address; @@ -106,8 +110,6 @@ mod templates; type Result = std::result::Result; -pub(crate) type InscriptionId = Txid; - const DIFFCHANGE_INTERVAL: u64 = bitcoin::blockdata::constants::DIFFCHANGE_INTERVAL as u64; const SUBSIDY_HALVING_INTERVAL: u64 = bitcoin::blockdata::constants::SUBSIDY_HALVING_INTERVAL as u64; diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index bb858bc620..99dc0016ba 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1,10 +1,7 @@ use super::*; use { - self::{ - deserialize_from_str::DeserializeFromStr, - error::{OptionExt, ServerError, ServerResult}, - }, + self::error::{OptionExt, ServerError, ServerResult}, crate::templates::{ BlockHtml, ClockSvg, HomeHtml, InputHtml, InscriptionHtml, InscriptionsHtml, OutputHtml, PageContent, PageHtml, PreviewImageHtml, PreviewTextHtml, PreviewUnknownHtml, RangeHtml, @@ -26,13 +23,11 @@ use { caches::DirCache, AcmeConfig, }, - serde::{de, Deserializer}, std::{cmp::Ordering, str}, tokio_stream::StreamExt, tower_http::set_header::SetResponseHeaderLayer, }; -mod deserialize_from_str; mod error; enum BlockQuery { @@ -423,8 +418,9 @@ impl Server { Extension(chain): Extension, Path(txid): Path, ) -> ServerResult> { + // TODO: don't get whole inscription let inscription = index - .get_inscription_by_id(txid)? + .get_inscription_by_id(txid.into())? .map(|(inscription, _satpoint)| inscription); Ok( @@ -432,7 +428,7 @@ impl Server { index .get_transaction(txid)? .ok_or_not_found(|| format!("transaction {txid}"))?, - inscription, + inscription.map(|_| txid.into()), chain, ) .page(chain, index.has_sat_index()?), diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index a88ba6bcc6..cd7eda05cf 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -370,7 +370,7 @@ mod tests { outpoint: outpoint(1), offset: 0, }, - Txid::from_str("06413a3ef4232f0485df2bc7c912c13c05c69f967c19639344753e05edb64bd5").unwrap(), + inscription_id(1), ); let inscription = inscription("text/plain", "ord"); @@ -409,7 +409,7 @@ mod tests { outpoint: outpoint(1), offset: 0, }, - Txid::from_str("06413a3ef4232f0485df2bc7c912c13c05c69f967c19639344753e05edb64bd5").unwrap(), + inscription_id(1), ); let inscription = inscription("text/plain", "ord"); diff --git a/src/templates/home.rs b/src/templates/home.rs index 94ab5fe138..eacc8b2c36 100644 --- a/src/templates/home.rs +++ b/src/templates/home.rs @@ -49,7 +49,7 @@ mod tests { .unwrap() ) ], - vec![txid(1), txid(2),], + vec![inscription_id(1), inscription_id(2)], ) .to_string(), "

Latest Inscriptions

diff --git a/src/templates/iframe.rs b/src/templates/iframe.rs index ff78affb18..2e9868b4a3 100644 --- a/src/templates/iframe.rs +++ b/src/templates/iframe.rs @@ -48,7 +48,7 @@ mod tests { #[test] fn preview() { assert_regex_match!( - Iframe::preview(txid(1)) + Iframe::preview(inscription_id(1)) .0.to_string(), "", ); @@ -57,7 +57,7 @@ mod tests { #[test] fn main() { assert_regex_match!( - Iframe::main(txid(1)) + Iframe::main(inscription_id(1)) .0.to_string(), "", ); diff --git a/src/templates/inscription.rs b/src/templates/inscription.rs index 47fc517914..d45c7167cd 100644 --- a/src/templates/inscription.rs +++ b/src/templates/inscription.rs @@ -35,7 +35,7 @@ mod tests { chain: Chain::Mainnet, genesis_height: 0, inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), - inscription_id: txid(1), + inscription_id: inscription_id(1), next: None, number: 1, output: tx_out(1, address()), @@ -86,7 +86,7 @@ mod tests { chain: Chain::Mainnet, genesis_height: 0, inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), - inscription_id: txid(1), + inscription_id: inscription_id(1), next: None, number: 1, output: tx_out(1, address()), @@ -116,11 +116,11 @@ mod tests { chain: Chain::Mainnet, genesis_height: 0, inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), - inscription_id: txid(2), - next: Some(txid(3)), + inscription_id: inscription_id(2), + next: Some(inscription_id(3)), number: 1, output: tx_out(1, address()), - previous: Some(txid(1)), + previous: Some(inscription_id(1)), sat: None, satpoint: satpoint(1, 0), }, diff --git a/src/templates/inscriptions.rs b/src/templates/inscriptions.rs index a22709cad6..02674bb983 100644 --- a/src/templates/inscriptions.rs +++ b/src/templates/inscriptions.rs @@ -19,7 +19,7 @@ mod tests { fn inscriptions() { assert_regex_match!( InscriptionsHtml { - inscriptions: vec![txid(1), txid(2)], + inscriptions: vec![inscription_id(1), inscription_id(2)], }, "

Inscriptions

diff --git a/src/templates/transaction.rs b/src/templates/transaction.rs index f49a46773e..df11c0f453 100644 --- a/src/templates/transaction.rs +++ b/src/templates/transaction.rs @@ -3,7 +3,7 @@ use super::*; #[derive(Boilerplate)] pub(crate) struct TransactionHtml { chain: Chain, - inscription: Option, + inscription: Option, transaction: Transaction, txid: Txid, } @@ -11,7 +11,7 @@ pub(crate) struct TransactionHtml { impl TransactionHtml { pub(crate) fn new( transaction: Transaction, - inscription: Option, + inscription: Option, chain: Chain, ) -> Self { Self { diff --git a/src/test.rs b/src/test.rs index e9ecf96f57..960043d2eb 100644 --- a/src/test.rs +++ b/src/test.rs @@ -80,3 +80,13 @@ pub(crate) fn tx_out(value: u64, address: Address) -> TxOut { pub(crate) fn inscription(content_type: &str, content: impl AsRef<[u8]>) -> Inscription { Inscription::new(Some(content_type.into()), Some(content.as_ref().into())) } + +pub(crate) fn inscription_id(n: u32) -> InscriptionId { + let hex = format!("{n:x}"); + + if hex.is_empty() || hex.len() > 1 { + panic!(); + } + + format!("{}i{n}", hex.repeat(64)).parse().unwrap() +} diff --git a/templates/transaction.html b/templates/transaction.html index 7d057df264..c016c98cfa 100644 --- a/templates/transaction.html +++ b/templates/transaction.html @@ -1,7 +1,7 @@

Transaction {{self.txid}}

-%% if let Some(inscription) = &self.inscription { +%% if let Some(id) = self.inscription {

Inscription Geneses

-{{ Iframe::preview(self.txid) }} +{{ Iframe::preview(id) }} %% }

{{"Output".tally(self.transaction.output.len())}}

    From b964c8b0cf947163b3cf4bfd0b8ac7a53d645ab3 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:21:50 -0800 Subject: [PATCH 02/22] Refactor parsing --- src/inscription_id.rs | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/inscription_id.rs b/src/inscription_id.rs index c5d3cbcd9b..2132455db1 100644 --- a/src/inscription_id.rs +++ b/src/inscription_id.rs @@ -29,32 +29,57 @@ impl Display for InscriptionId { } } +#[derive(Debug)] +pub(crate) enum ParseError { + Character(char), + Length(usize), + Separator(char), + Txid(bitcoin::hashes::hex::Error), + Index(std::num::ParseIntError), +} + +impl Display for ParseError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Character(c) => write!(f, "invalid character: '{c}'"), + Self::Length(len) => write!(f, "invalid length: {len}"), + Self::Separator(c) => write!(f, "invalid seprator: `{c}`"), + Self::Txid(err) => write!(f, "invalid txid: {err}"), + Self::Index(err) => write!(f, "invalid index: {err}"), + } + } +} + +impl std::error::Error for ParseError {} + impl FromStr for InscriptionId { - type Err = Error; + type Err = ParseError; - fn from_str(s: &str) -> Result { - if !s.is_ascii() { - bail!("invalid character"); + fn from_str(s: &str) -> Result { + if let Some(char) = s.chars().find(|char| !char.is_ascii()) { + return Err(ParseError::Character(char)); } const TXID_LEN: usize = 64; const MIN_LEN: usize = TXID_LEN + 2; if s.len() < MIN_LEN { - bail!("invalid length"); + return Err(ParseError::Length(s.len())); } let txid = &s[..TXID_LEN]; - if &s[TXID_LEN..TXID_LEN + 1] != "i" { - bail!("invalid separator"); + let separator = s.chars().nth(TXID_LEN).unwrap(); + + if separator != 'i' { + return Err(ParseError::Separator(separator)); } let vout = &s[TXID_LEN + 1..]; Ok(Self { - txid: txid.parse()?, - vout: vout.parse()?, + txid: txid.parse().map_err(ParseError::Txid)?, + vout: vout.parse().map_err(ParseError::Index)?, }) } } From bb3b16c11efeaa752a7b24bc56deee26de0e7781 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:28:12 -0800 Subject: [PATCH 03/22] fix outgoing test --- src/outgoing.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/outgoing.rs b/src/outgoing.rs index 5c0bfa1727..614209c6d3 100644 --- a/src/outgoing.rs +++ b/src/outgoing.rs @@ -11,10 +11,10 @@ impl FromStr for Outgoing { type Err = Error; fn from_str(s: &str) -> Result { - Ok(if s.len() == 64 { - Self::InscriptionId(s.parse()?) - } else if s.contains(':') { + Ok(if s.contains(':') { Self::SatPoint(s.parse()?) + } else if s.len() >= 66 { + Self::InscriptionId(s.parse()?) } else if s.contains(' ') { Self::Amount(s.parse()?) } else if let Some(i) = s.find(|c: char| c.is_alphabetic()) { @@ -34,11 +34,11 @@ mod tests { #[test] fn parse() { assert_eq!( - "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000i0" .parse::() .unwrap(), Outgoing::InscriptionId( - "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000i0" .parse() .unwrap() ), From 4fc62543a02f501df45c3a1807b1e9749bbe7ac0 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:31:41 -0800 Subject: [PATCH 04/22] Fix more tests --- src/subcommand/server.rs | 4 ++-- src/templates/home.rs | 4 ++-- src/templates/iframe.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 99dc0016ba..443ccdf9c0 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1706,7 +1706,7 @@ mod tests { let server = TestServer::new(); server.mine_blocks(1); - let inscription_id = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/html;charset=utf-8", "hello").to_witness(), ..Default::default() @@ -1715,7 +1715,7 @@ mod tests { server.mine_blocks(1); server.assert_response_csp( - format!("/preview/{inscription_id}"), + format!("/preview/{}", InscriptionId::from(txid)), StatusCode::OK, "default-src 'unsafe-eval' 'unsafe-inline'", "hello", diff --git a/src/templates/home.rs b/src/templates/home.rs index eacc8b2c36..818f5b6d6d 100644 --- a/src/templates/home.rs +++ b/src/templates/home.rs @@ -54,8 +54,8 @@ mod tests { .to_string(), "

    Latest Inscriptions

    - - + +

    Latest Blocks

    diff --git a/src/templates/iframe.rs b/src/templates/iframe.rs index 2e9868b4a3..e6a6b064ed 100644 --- a/src/templates/iframe.rs +++ b/src/templates/iframe.rs @@ -50,7 +50,7 @@ mod tests { assert_regex_match!( Iframe::preview(inscription_id(1)) .0.to_string(), - "", + "", ); } @@ -59,7 +59,7 @@ mod tests { assert_regex_match!( Iframe::main(inscription_id(1)) .0.to_string(), - "", + "", ); } } From fcc58a1fb3cc96a4d09d4a1fed41dc0cd89051ab Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:32:31 -0800 Subject: [PATCH 05/22] Fix tests --- src/templates/sat.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/templates/sat.rs b/src/templates/sat.rs index 44bea6ecd8..94d28662d7 100644 --- a/src/templates/sat.rs +++ b/src/templates/sat.rs @@ -102,13 +102,9 @@ mod tests { sat: Sat(0), satpoint: None, blocktime: Blocktime::Confirmed(0), - inscription: Some( - "1111111111111111111111111111111111111111111111111111111111111111" - .parse() - .unwrap(), - ), + inscription: Some(inscription_id(1)), }, - r"

    Sat 0

    .*
    inscription
    .*
    .*", + r"

    Sat 0

    .*
    inscription
    .*
    .*", ); } From a78f6040833e773e09af88f20015e32bcacd916b Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:33:49 -0800 Subject: [PATCH 06/22] Fix more tests --- src/subcommand/server.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 443ccdf9c0..3e8e7e4b09 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1660,7 +1660,7 @@ mod tests { let server = TestServer::new(); server.mine_blocks(1); - let inscription_id = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription( "text/plain;charset=utf-8", @@ -1673,7 +1673,7 @@ mod tests { server.mine_blocks(1); server.assert_response_csp( - format!("/preview/{inscription_id}"), + format!("/preview/{}", InscriptionId::from(txid)), StatusCode::OK, "default-src 'self'", r".*
    <script>alert\('hello'\);</script>
    .*", @@ -1685,11 +1685,12 @@ mod tests { let server = TestServer::new(); server.mine_blocks(1); - let inscription_id = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("image/png", "hello").to_witness(), ..Default::default() }); + let inscription_id = InscriptionId::from(txid); server.mine_blocks(1); From 4499477cba0a1b03f2269b4d68c276beb79ee14f Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:37:18 -0800 Subject: [PATCH 07/22] Fix more stuff --- src/index/array.rs | 8 ++++---- src/inscription_id.rs | 18 ++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/index/array.rs b/src/index/array.rs index 399c7f94a9..55123e95b3 100644 --- a/src/index/array.rs +++ b/src/index/array.rs @@ -12,18 +12,18 @@ impl Array for InscriptionId { type Array = InscriptionIdArray; fn from_array(array: Self::Array) -> Self { - let (txid, vout) = array.split_at(32); + let (txid, index) = array.split_at(32); Self { txid: Txid::from_inner(txid.try_into().unwrap()), - vout: u32::from_be_bytes(vout.try_into().unwrap()), + index: u32::from_be_bytes(index.try_into().unwrap()), } } fn array(self) -> Self::Array { let mut array = [0; 36]; - let (txid, vout) = array.split_at_mut(32); + let (txid, index) = array.split_at_mut(32); txid.copy_from_slice(self.txid.as_inner()); - vout.copy_from_slice(&self.vout.to_be_bytes()); + index.copy_from_slice(&self.index.to_be_bytes()); array } } diff --git a/src/inscription_id.rs b/src/inscription_id.rs index 2132455db1..e76ff57478 100644 --- a/src/inscription_id.rs +++ b/src/inscription_id.rs @@ -2,7 +2,6 @@ use super::*; // TODO: // - fix tests -// - use TXIDiINSCRIPTION_NUMBER // - print inscription ID on send // - make parse recognize inscription IDs // - make sure we use inscriptionIDs in index methods @@ -10,8 +9,7 @@ use super::*; #[derive(Debug, PartialEq, Copy, Clone)] pub(crate) struct InscriptionId { pub(crate) txid: Txid, - // TODO: rename to index, i, or inscription - pub(crate) vout: u32, + pub(crate) index: u32, } impl<'de> Deserialize<'de> for InscriptionId { @@ -25,7 +23,7 @@ impl<'de> Deserialize<'de> for InscriptionId { impl Display for InscriptionId { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}i{}", self.txid, self.vout) + write!(f, "{}i{}", self.txid, self.index) } } @@ -79,14 +77,14 @@ impl FromStr for InscriptionId { Ok(Self { txid: txid.parse().map_err(ParseError::Txid)?, - vout: vout.parse().map_err(ParseError::Index)?, + index: vout.parse().map_err(ParseError::Index)?, }) } } impl From for InscriptionId { fn from(txid: Txid) -> Self { - Self { txid, vout: 0 } + Self { txid, index: 0 } } } @@ -103,7 +101,7 @@ mod tests { assert_eq!( InscriptionId { txid: txid(1), - vout: 0, + index: 0, } .to_string(), "1111111111111111111111111111111111111111111111111111111111111111i0", @@ -111,7 +109,7 @@ mod tests { assert_eq!( InscriptionId { txid: txid(1), - vout: 0xFFFFFFFF, + index: 0xFFFFFFFF, } .to_string(), "1111111111111111111111111111111111111111111111111111111111111111i4294967295", @@ -132,7 +130,7 @@ mod tests { .unwrap(), InscriptionId { txid: txid(1), - vout: 0xFFFFFFFF, + index: 0xFFFFFFFF, }, ); assert_eq!( @@ -141,7 +139,7 @@ mod tests { .unwrap(), InscriptionId { txid: txid(1), - vout: 0xFFFFFFFF, + index: 0xFFFFFFFF, }, ); } From e5e53ff9606144a8239c6ca4af47b3cb94d59854 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:42:59 -0800 Subject: [PATCH 08/22] Fix more tests --- src/subcommand/server.rs | 8 ++++---- src/templates/inscription.rs | 12 ++++++------ templates/inscription.html | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 3e8e7e4b09..639479478e 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1639,7 +1639,7 @@ mod tests { let server = TestServer::new(); server.mine_blocks(1); - let inscription_id = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/plain;charset=utf-8", "hello").to_witness(), ..Default::default() @@ -1648,7 +1648,7 @@ mod tests { server.mine_blocks(1); server.assert_response_csp( - format!("/preview/{inscription_id}"), + format!("/preview/{}", InscriptionId::from(txid)), StatusCode::OK, "default-src 'self'", ".*
    hello
    .*", @@ -1728,7 +1728,7 @@ mod tests { let server = TestServer::new(); server.mine_blocks(1); - let inscription_id = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/foo", "hello").to_witness(), ..Default::default() @@ -1737,7 +1737,7 @@ mod tests { server.mine_blocks(1); server.assert_response_csp( - format!("/preview/{inscription_id}"), + format!("/preview/{}", InscriptionId::from(txid)), StatusCode::OK, "default-src 'self'", fs::read_to_string("templates/preview-unknown.html").unwrap(), diff --git a/src/templates/inscription.rs b/src/templates/inscription.rs index d45c7167cd..413ac6a4cd 100644 --- a/src/templates/inscription.rs +++ b/src/templates/inscription.rs @@ -47,18 +47,18 @@ mod tests {

    Inscription 1

    - +
    id
    -
    1{64}
    +
    1{64}i1
    address
    bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
    output value
    1
    content
    -
    link
    +
    link
    content size
    10 bytes
    content type
    @@ -127,9 +127,9 @@ mod tests { "

    Inscription 1

    - - - + + +
    .* " diff --git a/templates/inscription.html b/templates/inscription.html index b54e0d1728..730a81397f 100644 --- a/templates/inscription.html +++ b/templates/inscription.html @@ -38,7 +38,7 @@

    Inscription {{ self.number }}

    genesis height
    {{ self.genesis_height }}
    genesis transaction
    -
    {{ self.inscription_id }}
    +
    {{ self.inscription_id.txid }}
    location
    {{ self.satpoint }}
    output
    From bdae009bd811d0c089c555db6b229128d5275ef5 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 11 Jan 2023 21:52:37 -0800 Subject: [PATCH 09/22] tweak --- tests/wallet/inscriptions.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index 6265445fe2..6b0f14b268 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -4,21 +4,20 @@ use super::*; fn inscriptions() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + rpc_server.mine_blocks(1); - let inscription_id = reveal_txid_from_inscribe_stdout( - &CommandBuilder::new(format!("wallet inscribe --satpoint {txid}:0:0 hello.txt")) + let txid = reveal_txid_from_inscribe_stdout( + &CommandBuilder::new(format!("wallet inscribe hello.txt")) .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(), ); - rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscriptions") .rpc_server(&rpc_server) - .expected_stdout(format!("{inscription_id}\t{inscription_id}:0:0\n")) + .expected_stdout(format!("{txid}i0\t{txid}:0:0\n")) .run(); let stdout = CommandBuilder::new("wallet receive") @@ -29,7 +28,7 @@ fn inscriptions() { let address = stdout.trim(); - let stdout = CommandBuilder::new(format!("wallet send {address} {inscription_id}")) + let stdout = CommandBuilder::new(format!("wallet send {address} {txid}i0")) .rpc_server(&rpc_server) .expected_exit_code(0) .stdout_regex(".*") @@ -43,7 +42,7 @@ fn inscriptions() { CommandBuilder::new("wallet inscriptions") .rpc_server(&rpc_server) - .expected_stdout(format!("{inscription_id}\t{outpoint}:0\n")) + .expected_stdout(format!("{txid}i0\t{outpoint}:0\n")) .run(); } From 945b11053fa55fb1c6a8d6630a8350222c40e830 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 01:33:46 -0800 Subject: [PATCH 10/22] Finish fixing unit tests --- src/subcommand/server.rs | 8 ++++---- src/subcommand/wallet/transaction_builder.rs | 18 +++--------------- src/templates/inscriptions.rs | 4 ++-- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 639479478e..e498e4fff7 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1749,7 +1749,7 @@ mod tests { let server = TestServer::new_with_args(&["--index-sats"]); server.mine_blocks(1); - let inscription_id = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/foo", "hello").to_witness(), ..Default::default() @@ -1758,7 +1758,7 @@ mod tests { server.mine_blocks(1); server.assert_response_regex( - format!("/inscription/{inscription_id}"), + format!("/inscription/{}", InscriptionId::from(txid)), StatusCode::OK, r".*
    sat
    \s*
    5000000000
    \s*
    content
    .*", ); @@ -1769,7 +1769,7 @@ mod tests { let server = TestServer::new(); server.mine_blocks(1); - let inscription_id = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], witness: inscription("text/foo", "hello").to_witness(), ..Default::default() @@ -1778,7 +1778,7 @@ mod tests { server.mine_blocks(1); server.assert_response_regex( - format!("/inscription/{inscription_id}"), + format!("/inscription/{}", InscriptionId::from(txid)), StatusCode::OK, r".*
    output value
    \s*
    5000000000
    \s*
    content
    .*", ); diff --git a/src/subcommand/wallet/transaction_builder.rs b/src/subcommand/wallet/transaction_builder.rs index 40ab1d9925..b2e92c0457 100644 --- a/src/subcommand/wallet/transaction_builder.rs +++ b/src/subcommand/wallet/transaction_builder.rs @@ -1030,12 +1030,7 @@ mod tests { pretty_assert_eq!( TransactionBuilder::build_transaction( satpoint(1, 0), - BTreeMap::from([( - satpoint(2, 10 * COIN_VALUE), - "bed200b55adcf20e359bbb762392d5106cafbafc48e55f77c94d3041de3521da" - .parse() - .unwrap() - )]), + BTreeMap::from([(satpoint(2, 10 * COIN_VALUE), inscription_id(1),)]), utxos.into_iter().collect(), recipient(), vec![change(0), change(1)], @@ -1051,12 +1046,7 @@ mod tests { pretty_assert_eq!( TransactionBuilder::build_transaction( satpoint(1, 0), - BTreeMap::from([( - satpoint(1, 500), - "bed200b55adcf20e359bbb762392d5106cafbafc48e55f77c94d3041de3521da" - .parse() - .unwrap() - )]), + BTreeMap::from([(satpoint(1, 500), inscription_id(1))]), utxos.into_iter().collect(), recipient(), vec![change(0), change(1)], @@ -1064,9 +1054,7 @@ mod tests { Err(Error::UtxoContainsAdditionalInscription { outgoing_satpoint: satpoint(1, 0), inscribed_satpoint: satpoint(1, 500), - inscription_id: "bed200b55adcf20e359bbb762392d5106cafbafc48e55f77c94d3041de3521da" - .parse() - .unwrap(), + inscription_id: inscription_id(1), }) ) } diff --git a/src/templates/inscriptions.rs b/src/templates/inscriptions.rs index 02674bb983..9803a00dbf 100644 --- a/src/templates/inscriptions.rs +++ b/src/templates/inscriptions.rs @@ -24,8 +24,8 @@ mod tests { "

    Inscriptions

    - - + +
    " .unindent() From d9ca60d0572a8e4f5e3c7b576c7162144067c6a8 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 02:02:05 -0800 Subject: [PATCH 11/22] Fix test --- src/subcommand/wallet/send.rs | 6 ++-- tests/lib.rs | 10 +++---- tests/server.rs | 12 ++++---- tests/wallet/inscribe.rs | 25 ++++++++--------- tests/wallet/inscriptions.rs | 19 ++++++------- tests/wallet/send.rs | 53 +++++++++++------------------------ 6 files changed, 51 insertions(+), 74 deletions(-) diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 2934270da3..7bbbb04ab5 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -40,10 +40,10 @@ impl Send { } satpoint } - Outgoing::InscriptionId(txid) => index - .get_inscription_by_id(txid)? + Outgoing::InscriptionId(id) => index + .get_inscription_by_id(id)? .map(|(_inscription, satpoint)| satpoint) - .ok_or_else(|| anyhow!("No inscription found for {txid}"))?, + .ok_or_else(|| anyhow!("Inscription {id} not found"))?, Outgoing::Amount(amount) => { let all_inscription_outputs = inscriptions .keys() diff --git a/tests/lib.rs b/tests/lib.rs index ff2ebb8fe1..4516b772fd 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -47,11 +47,11 @@ fn reveal_txid_from_inscribe_stdout(stdout: &str) -> Txid { .unwrap() } -fn create_inscription(rpc_server: &test_bitcoincore_rpc::Handle, filename: &str) -> Txid { - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); +fn create_inscription(rpc_server: &test_bitcoincore_rpc::Handle, filename: &str) -> String { + rpc_server.mine_blocks(1); let stdout = CommandBuilder::new(format!( - "--chain {} wallet inscribe --satpoint {txid}:0:0 {filename}", + "--chain {} wallet inscribe {filename}", rpc_server.network() )) .write(filename, "HELLOWORLD") @@ -59,11 +59,11 @@ fn create_inscription(rpc_server: &test_bitcoincore_rpc::Handle, filename: &str) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); - let inscription_id = reveal_txid_from_inscribe_stdout(&stdout); + let reveal_txid = reveal_txid_from_inscribe_stdout(&stdout); rpc_server.mine_blocks(1); - inscription_id + format!("{reveal_txid}i0") } fn create_wallet(rpc_server: &test_bitcoincore_rpc::Handle) { diff --git a/tests/server.rs b/tests/server.rs index 5793fa70bb..869ae37924 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -131,9 +131,9 @@ fn inscription_page_after_send() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + rpc_server.mine_blocks(1); - let stdout = CommandBuilder::new(format!("wallet inscribe --satpoint {txid}:0:0 hello.txt")) + let stdout = CommandBuilder::new(format!("wallet inscribe hello.txt")) .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") @@ -141,18 +141,20 @@ fn inscription_page_after_send() { let reveal_txid = reveal_txid_from_inscribe_stdout(&stdout); + let inscription_id = format!("{reveal_txid}i0"); + rpc_server.mine_blocks(1); let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); ord_server.assert_response_regex( - format!("/inscription/{reveal_txid}"), + format!("/inscription/{inscription_id}"), format!( r".*

    Inscription 0

    .*
    location
    \s*
    {reveal_txid}:0:0
    .*", ), ); let txid = CommandBuilder::new(format!( - "wallet send --cardinal bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {reveal_txid}" + "wallet send --cardinal bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {inscription_id}" )) .rpc_server(&rpc_server) .stdout_regex(".*") @@ -164,7 +166,7 @@ fn inscription_page_after_send() { let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); ord_server.assert_response_regex( - format!("/inscription/{reveal_txid}"), + format!("/inscription/{inscription_id}"), format!( r".*

    Inscription 0

    .*
    address
    \s*
    bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv
    .*
    location
    \s*
    {send_txid}:0:0
    .*", ), diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 9e84be21f1..0fbc7c9a38 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -36,24 +36,18 @@ fn inscribe_no_backup() { #[test] fn inscribe() { let rpc_server = test_bitcoincore_rpc::spawn(); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + rpc_server.mine_blocks(1); assert_eq!(rpc_server.descriptors().len(), 0); create_wallet(&rpc_server); - let stdout = CommandBuilder::new(format!("wallet inscribe --satpoint {txid}:0:0 hello.txt")) - .write("hello.txt", "HELLOWORLD") - .rpc_server(&rpc_server) - .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") - .run(); + assert_eq!(rpc_server.descriptors().len(), 2); - let inscription_id = reveal_txid_from_inscribe_stdout(&stdout); + let inscription_id = create_inscription(&rpc_server, "foo.txt"); assert_eq!(rpc_server.descriptors().len(), 3); - rpc_server.mine_blocks(1); - let request = TestServer::spawn_with_args(&rpc_server, &[]).request(&format!("/content/{inscription_id}")); @@ -194,6 +188,7 @@ fn refuse_to_inscribe_already_inscribed_utxo() { create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let stdout = CommandBuilder::new(format!( "wallet inscribe --satpoint {txid}:0:0 degenerate.png" )) @@ -204,7 +199,7 @@ fn refuse_to_inscribe_already_inscribed_utxo() { rpc_server.mine_blocks(1); - let inscription_id = reveal_txid_from_inscribe_stdout(&stdout); + let reveal_txid = reveal_txid_from_inscribe_stdout(&stdout); let inscription_utxo = OutPoint { txid: reveal_txid_from_inscribe_stdout(&stdout), @@ -218,7 +213,7 @@ fn refuse_to_inscribe_already_inscribed_utxo() { .rpc_server(&rpc_server) .expected_exit_code(1) .expected_stderr(format!( - "error: utxo {inscription_utxo} already inscribed with inscription {inscription_id} on sat {inscription_utxo}:0\n", + "error: utxo {inscription_utxo} already inscribed with inscription {reveal_txid}i0 on sat {inscription_utxo}:0\n", )) .run(); } @@ -227,15 +222,17 @@ fn refuse_to_inscribe_already_inscribed_utxo() { fn inscribe_with_optional_satpoint_arg() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - let stdout = CommandBuilder::new("wallet inscribe hello.txt") + let stdout = CommandBuilder::new(format!("wallet inscribe hello.txt --satpoint {txid}:0:0")) .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); - let inscription_id = reveal_txid_from_inscribe_stdout(&stdout); + let reveal_txid = reveal_txid_from_inscribe_stdout(&stdout); + + let inscription_id = format!("{reveal_txid}i0"); rpc_server.mine_blocks(1); diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index 6b0f14b268..9dd4c7d7bd 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -6,7 +6,7 @@ fn inscriptions() { create_wallet(&rpc_server); rpc_server.mine_blocks(1); - let txid = reveal_txid_from_inscribe_stdout( + let reveal_txid = reveal_txid_from_inscribe_stdout( &CommandBuilder::new(format!("wallet inscribe hello.txt")) .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) @@ -15,9 +15,11 @@ fn inscriptions() { ); rpc_server.mine_blocks(1); + let inscription_id = format!("{reveal_txid}i0"); + CommandBuilder::new("wallet inscriptions") .rpc_server(&rpc_server) - .expected_stdout(format!("{txid}i0\t{txid}:0:0\n")) + .expected_stdout(format!("{inscription_id}\t{reveal_txid}:0:0\n")) .run(); let stdout = CommandBuilder::new("wallet receive") @@ -28,7 +30,7 @@ fn inscriptions() { let address = stdout.trim(); - let stdout = CommandBuilder::new(format!("wallet send {address} {txid}i0")) + let stdout = CommandBuilder::new(format!("wallet send {address} {inscription_id}")) .rpc_server(&rpc_server) .expected_exit_code(0) .stdout_regex(".*") @@ -42,7 +44,7 @@ fn inscriptions() { CommandBuilder::new("wallet inscriptions") .rpc_server(&rpc_server) - .expected_stdout(format!("{txid}i0\t{outpoint}:0\n")) + .expected_stdout(format!("{inscription_id}\t{outpoint}:0\n")) .run(); } @@ -53,7 +55,7 @@ fn inscriptions_includes_locked_utxos() { rpc_server.mine_blocks(1); - let inscription_id = reveal_txid_from_inscribe_stdout( + let txid = reveal_txid_from_inscribe_stdout( &CommandBuilder::new("wallet inscribe hello.txt") .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) @@ -63,13 +65,10 @@ fn inscriptions_includes_locked_utxos() { rpc_server.mine_blocks(1); - rpc_server.lock(OutPoint { - txid: inscription_id, - vout: 0, - }); + rpc_server.lock(OutPoint { txid, vout: 0 }); CommandBuilder::new("wallet inscriptions") .rpc_server(&rpc_server) - .expected_stdout(format!("{inscription_id}\t{inscription_id}:0:0\n")) + .expected_stdout(format!("{txid}i0\t{txid}:0:0\n")) .run(); } diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index b6ffae7e2c..df9f81ba8a 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -4,23 +4,14 @@ use super::*; fn send_works() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); + rpc_server.mine_blocks(1); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - - let stdout = CommandBuilder::new(format!( - "--index-sats wallet inscribe --satpoint {txid}:0:0 degenerate.png" - )) - .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) - .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") - .run(); - - let reveal_txid = reveal_txid_from_inscribe_stdout(&stdout); + let inscription_id = create_inscription(&rpc_server, "foo.txt"); rpc_server.mine_blocks(1); let stdout = CommandBuilder::new(format!( - "wallet send ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw {reveal_txid}" + "wallet send ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw {inscription_id}" )) .rpc_server(&rpc_server) .stdout_regex(r".*") @@ -35,13 +26,13 @@ fn send_works() { let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); ord_server.assert_response_regex( - format!("/inscription/{reveal_txid}"), + format!("/inscription/{inscription_id}"), format!( ".*

    Inscription 0

    .*
    .*
    content size
    -
    520 bytes
    +
    10 bytes
    content type
    -
    image/png
    +
    text/plain;charset=utf-8
    .*
    location
    {send_txid}:0:0
    @@ -54,44 +45,32 @@ fn send_works() { #[test] fn send_unknown_inscription() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Signet) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new(format!( - "--chain signet wallet send tord1q497kurvh0fgtedca5angel7j4rdwe0q8h925u0 {txid}" + "wallet send ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw {txid}i0" )) .rpc_server(&rpc_server) - .expected_stderr(format!("error: No inscription found for {txid}\n")) + .expected_stderr(format!("error: Inscription {txid}i0 not found\n")) .expected_exit_code(1) .run(); } #[test] fn send_inscribed_sat() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Signet) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + rpc_server.mine_blocks(1); - let stdout = CommandBuilder::new(format!( - "--chain signet --index-sats wallet inscribe --satpoint {txid}:0:0 degenerate.png" - )) - .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) - .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") - .run(); + let inscription_id = create_inscription(&rpc_server, "foo.txt"); rpc_server.mine_blocks(1); - let reveal_txid = reveal_txid_from_inscribe_stdout(&stdout); - let stdout = CommandBuilder::new(format!( - "--chain signet wallet send tord1q497kurvh0fgtedca5angel7j4rdwe0q8h925u0 {reveal_txid}" + "wallet send ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw {inscription_id}" )) .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\n") @@ -103,7 +82,7 @@ fn send_inscribed_sat() { let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); ord_server.assert_response_regex( - format!("/inscription/{reveal_txid}"), + format!("/inscription/{inscription_id}"), format!( ".*

    Inscription 0

    .*
    location
    .*
    {send_txid}:0:0
    .*", ), @@ -215,7 +194,7 @@ fn do_not_accidentally_send_an_inscription() { .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); - let inscription_id = reveal_txid_from_inscribe_stdout(&stdout); + let reveal_txid = reveal_txid_from_inscribe_stdout(&stdout); rpc_server.mine_blocks(1); @@ -230,7 +209,7 @@ fn do_not_accidentally_send_an_inscription() { .rpc_server(&rpc_server) .expected_exit_code(1) .expected_stderr(format!( - "error: cannot send {inscription_utxo}:55 without also sending inscription {inscription_id} at {inscription_utxo}:0\n" + "error: cannot send {inscription_utxo}:55 without also sending inscription {reveal_txid}i0 at {inscription_utxo}:0\n" )) .run(); } From 033b0be785a3010e3b059ced0b573a6a796379a9 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 02:03:43 -0800 Subject: [PATCH 12/22] Fix test --- tests/server.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/server.rs b/tests/server.rs index 869ae37924..886bd6ff2a 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -178,20 +178,14 @@ fn inscription_content() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - - let stdout = CommandBuilder::new(format!("wallet inscribe --satpoint {txid}:0:0 hello.txt")) - .write("hello.txt", "HELLOWORLD") - .rpc_server(&rpc_server) - .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") - .run(); + rpc_server.mine_blocks(1); - let reveal_tx = reveal_txid_from_inscribe_stdout(&stdout); + let inscription_id = create_inscription(&rpc_server, "foo.txt"); rpc_server.mine_blocks(1); let response = - TestServer::spawn_with_args(&rpc_server, &[]).request(&format!("/content/{reveal_tx}")); + TestServer::spawn_with_args(&rpc_server, &[]).request(&format!("/content/{inscription_id}")); assert_eq!(response.status(), StatusCode::OK); assert_eq!( From d3692ee06afed0d15d8ac51fea7eec8417534625 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 02:21:49 -0800 Subject: [PATCH 13/22] Finish fixing tests T_T --- tests/server.rs | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/tests/server.rs b/tests/server.rs index 886bd6ff2a..c70e92d2ef 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -38,9 +38,9 @@ fn run() { fn inscription_page() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + rpc_server.mine_blocks(1); - let stdout = CommandBuilder::new(format!("wallet inscribe --satpoint {txid}:0:0 hello.txt")) + let stdout = CommandBuilder::new(format!("wallet inscribe hello.txt")) .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") @@ -48,21 +48,25 @@ fn inscription_page() { let reveal_tx = reveal_txid_from_inscribe_stdout(&stdout); + let inscription_id = format!("{reveal_tx}i0"); + rpc_server.mine_blocks(1); TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( - format!("/inscription/{reveal_tx}"), + format!("/inscription/{inscription_id}"), format!( - ".*.* + ".*.*

    Inscription 0

    -.*.* +.*.*
    id
    -
    {reveal_tx}
    +
    {inscription_id}
    address
    bc1.*
    +
    output value
    +
    9860
    content
    -
    link
    +
    link
    content size
    10 bytes
    content type
    @@ -244,24 +248,16 @@ fn inscriptions_page() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - - let stdout = CommandBuilder::new(format!("wallet inscribe --satpoint {txid}:0:0 hello.txt")) - .write("hello.txt", "HELLOWORLD") - .rpc_server(&rpc_server) - .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") - .run(); - - let reveal_tx = reveal_txid_from_inscribe_stdout(&stdout); + let inscription_id = create_inscription(&rpc_server, "foo.png"); - rpc_server.mine_blocks(1); + // rpc_server.mine_blocks(1); TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( "/inscriptions", format!( ".*

    Inscriptions

    - .* + .*
    .*", ), From 4f62ead093846b5beee0c6f8a4be7af898ba9787 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 02:26:01 -0800 Subject: [PATCH 14/22] Implement parsing --- src/inscription_id.rs | 2 -- src/object.rs | 12 ++++++++++++ src/representation.rs | 3 +++ tests/server.rs | 2 -- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/inscription_id.rs b/src/inscription_id.rs index e76ff57478..5e60abbb62 100644 --- a/src/inscription_id.rs +++ b/src/inscription_id.rs @@ -1,9 +1,7 @@ use super::*; // TODO: -// - fix tests // - print inscription ID on send -// - make parse recognize inscription IDs // - make sure we use inscriptionIDs in index methods #[derive(Debug, PartialEq, Copy, Clone)] diff --git a/src/object.rs b/src/object.rs index ce196c0401..bc96576376 100644 --- a/src/object.rs +++ b/src/object.rs @@ -4,6 +4,7 @@ use super::*; pub(crate) enum Object { CardinalAddress(Address), Hash([u8; 32]), + InscriptionId(InscriptionId), Integer(u128), OrdinalAddress(OrdinalAddress), OutPoint(OutPoint), @@ -23,6 +24,7 @@ impl FromStr for Object { Hash => Ok(Self::Hash( bitcoin::hashes::sha256::Hash::from_str(s)?.into_inner(), )), + InscriptionId => Ok(Self::InscriptionId(s.parse()?)), Integer => Ok(Self::Integer(s.parse()?)), OrdinalAddress => Ok(Self::OrdinalAddress(s.parse()?)), OutPoint => Ok(Self::OutPoint(s.parse()?)), @@ -41,6 +43,7 @@ impl Display for Object { } Ok(()) } + Self::InscriptionId(inscription_id) => write!(f, "{inscription_id}"), Self::Integer(integer) => write!(f, "{integer}"), Self::OrdinalAddress(ordinal_address) => write!(f, "{}", ordinal_address), Self::OutPoint(outpoint) => write!(f, "{}", outpoint), @@ -81,6 +84,15 @@ mod tests { case("0", Object::Integer(0)); + case( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefi1", + Object::InscriptionId( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefi1" + .parse() + .unwrap(), + ), + ); + case( "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", Object::Hash([ diff --git a/src/representation.rs b/src/representation.rs index 06a330a163..77ea9e7fe2 100644 --- a/src/representation.rs +++ b/src/representation.rs @@ -6,6 +6,7 @@ pub(crate) enum Representation { Decimal, Degree, Hash, + InscriptionId, Integer, Name, OrdinalAddress, @@ -23,6 +24,7 @@ impl Representation { Self::Decimal => r"^.*\..*$", Self::Degree => r"^.*°.*′.*″(.*‴)?$", Self::Hash => r"^[[:xdigit:]]{64}$", + Self::InscriptionId => r"^[[:xdigit:]]{64}i\d+$", Self::Integer => r"^[0-9]*$", Self::Name => r"^[a-z]{1,11}$", Self::OrdinalAddress => OrdinalAddress::PATTERN, @@ -51,6 +53,7 @@ const PATTERNS: &[(Representation, &str)] = &[ Representation::Decimal.pattern(), Representation::Degree.pattern(), Representation::Hash.pattern(), + Representation::InscriptionId.pattern(), Representation::Integer.pattern(), Representation::Name.pattern(), Representation::OrdinalAddress.pattern(), diff --git a/tests/server.rs b/tests/server.rs index c70e92d2ef..423368b1a1 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -250,8 +250,6 @@ fn inscriptions_page() { let inscription_id = create_inscription(&rpc_server, "foo.png"); - // rpc_server.mine_blocks(1); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( "/inscriptions", format!( From d4741b00a957216e96d6811664d66ca970f9e03b Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 09:57:08 -0800 Subject: [PATCH 15/22] placate clippy --- tests/server.rs | 4 ++-- tests/wallet/inscriptions.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/server.rs b/tests/server.rs index 423368b1a1..2f6c240899 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -40,7 +40,7 @@ fn inscription_page() { create_wallet(&rpc_server); rpc_server.mine_blocks(1); - let stdout = CommandBuilder::new(format!("wallet inscribe hello.txt")) + let stdout = CommandBuilder::new("wallet inscribe hello.txt") .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") @@ -137,7 +137,7 @@ fn inscription_page_after_send() { rpc_server.mine_blocks(1); - let stdout = CommandBuilder::new(format!("wallet inscribe hello.txt")) + let stdout = CommandBuilder::new("wallet inscribe hello.txt") .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index 9dd4c7d7bd..b81503b1fc 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -7,7 +7,7 @@ fn inscriptions() { rpc_server.mine_blocks(1); let reveal_txid = reveal_txid_from_inscribe_stdout( - &CommandBuilder::new(format!("wallet inscribe hello.txt")) + &CommandBuilder::new("wallet inscribe hello.txt") .write("hello.txt", "HELLOWORLD") .rpc_server(&rpc_server) .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") From 6f969a4e86ffd1bc5d5dd34bf944f042a209d883 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 10:00:28 -0800 Subject: [PATCH 16/22] Make note --- src/inscription_id.rs | 1 + src/subcommand/server.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inscription_id.rs b/src/inscription_id.rs index 5e60abbb62..60f6536c33 100644 --- a/src/inscription_id.rs +++ b/src/inscription_id.rs @@ -3,6 +3,7 @@ use super::*; // TODO: // - print inscription ID on send // - make sure we use inscriptionIDs in index methods +// - test errors #[derive(Debug, PartialEq, Copy, Clone)] pub(crate) struct InscriptionId { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index e498e4fff7..4320c8250f 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -418,7 +418,6 @@ impl Server { Extension(chain): Extension, Path(txid): Path, ) -> ServerResult> { - // TODO: don't get whole inscription let inscription = index .get_inscription_by_id(txid.into())? .map(|(inscription, _satpoint)| inscription); From ffb3e9dd05cc895e1a4cc367e15f33fab9a93dcc Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 12:03:00 -0800 Subject: [PATCH 17/22] fix --- src/index.rs | 95 +++++++++++------------- src/index/array.rs | 59 --------------- src/index/inscription_entry.rs | 37 --------- src/index/updater.rs | 19 ++--- src/index/updater/inscription_updater.rs | 32 ++++---- 5 files changed, 66 insertions(+), 176 deletions(-) delete mode 100644 src/index/array.rs delete mode 100644 src/index/inscription_entry.rs diff --git a/src/index.rs b/src/index.rs index 4e3f31a104..b4d35d3db1 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1,7 +1,9 @@ use { self::{ - array::Array, - inscription_entry::{InscriptionEntry, InscriptionEntryValue}, + entry::{ + BlockHashValue, Entry, InscriptionEntry, InscriptionEntryValue, InscriptionIdValue, + OutPointValue, SatPointValue, SatRangeValue, + }, updater::Updater, }, super::*, @@ -14,17 +16,10 @@ use { std::sync::atomic::{self, AtomicBool}, }; -mod array; -mod inscription_entry; +mod entry; mod rtx; mod updater; -type BlockHashArray = [u8; 32]; -type InscriptionIdArray = [u8; 36]; -type OutPointArray = [u8; 36]; -type SatPointArray = [u8; 44]; -type SatRangeArray = [u8; 11]; - const SCHEMA_VERSION: u64 = 1; macro_rules! define_table { @@ -33,15 +28,15 @@ macro_rules! define_table { }; } -define_table! { HEIGHT_TO_BLOCK_HASH, u64, &BlockHashArray } -define_table! { INSCRIPTION_ID_TO_INSCRIPTION_ENTRY, &InscriptionIdArray, InscriptionEntryValue } -define_table! { INSCRIPTION_ID_TO_SATPOINT, &InscriptionIdArray, &SatPointArray } -define_table! { INSCRIPTION_NUMBER_TO_INSCRIPTION_ID, u64, &InscriptionIdArray } -define_table! { OUTPOINT_TO_SAT_RANGES, &OutPointArray, &[u8] } -define_table! { OUTPOINT_TO_VALUE, &OutPointArray, u64} -define_table! { SATPOINT_TO_INSCRIPTION_ID, &SatPointArray, &InscriptionIdArray } -define_table! { SAT_TO_INSCRIPTION_ID, u64, &InscriptionIdArray } -define_table! { SAT_TO_SATPOINT, u64, &SatPointArray } +define_table! { HEIGHT_TO_BLOCK_HASH, u64, &BlockHashValue } +define_table! { INSCRIPTION_ID_TO_INSCRIPTION_ENTRY, &InscriptionIdValue, InscriptionEntryValue } +define_table! { INSCRIPTION_ID_TO_SATPOINT, &InscriptionIdValue, &SatPointValue } +define_table! { INSCRIPTION_NUMBER_TO_INSCRIPTION_ID, u64, &InscriptionIdValue } +define_table! { OUTPOINT_TO_SAT_RANGES, &OutPointValue, &[u8] } +define_table! { OUTPOINT_TO_VALUE, &OutPointValue, u64} +define_table! { SATPOINT_TO_INSCRIPTION_ID, &SatPointValue, &InscriptionIdValue } +define_table! { SAT_TO_INSCRIPTION_ID, u64, &InscriptionIdValue } +define_table! { SAT_TO_SATPOINT, u64, &SatPointValue } define_table! { STATISTIC_TO_COUNT, u64, u64 } define_table! { WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP, u64, u128 } @@ -310,7 +305,7 @@ impl Index { Ok(info) } - pub(crate) fn decode_sat_range(bytes: SatRangeArray) -> (u64, u64) { + pub(crate) fn decode_sat_range(bytes: SatRangeValue) -> (u64, u64) { let raw_base = u64::from_le_bytes([ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], 0, ]); @@ -392,7 +387,7 @@ impl Index { let height_to_block_hash = rtx.0.open_table(HEIGHT_TO_BLOCK_HASH)?; for next in height_to_block_hash.range(0..block_count)?.rev().take(take) { - blocks.push((next.0.value(), BlockHash::from_slice(next.1.value())?)); + blocks.push((next.0.value(), BlockHash::load(*next.1.value()))); } Ok(blocks) @@ -407,7 +402,7 @@ impl Index { let sat_to_satpoint = rtx.open_table(SAT_TO_SATPOINT)?; for (sat, satpoint) in sat_to_satpoint.range(0..)? { - result.push((Sat(sat.value()), Array::from_array(*satpoint.value()))); + result.push((Sat(sat.value()), Entry::load(*satpoint.value()))); } Ok(Some(result)) @@ -424,7 +419,7 @@ impl Index { .begin_read()? .open_table(SAT_TO_SATPOINT)? .get(&sat.n())? - .map(|satpoint| Array::from_array(*satpoint.value())), + .map(|satpoint| Entry::load(*satpoint.value())), ) } else { Ok(None) @@ -461,7 +456,7 @@ impl Index { .begin_read()? .open_table(SAT_TO_INSCRIPTION_ID)? .get(&sat.n())? - .map(|inscription_id| Array::from_array(*inscription_id.value())), + .map(|inscription_id| Entry::load(*inscription_id.value())), ) } @@ -475,7 +470,7 @@ impl Index { .begin_read()? .open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)? .get(&n)? - .map(|id| Array::from_array(*id.value())), + .map(|id| Entry::load(*id.value())), ) } @@ -488,8 +483,8 @@ impl Index { .database .begin_read()? .open_table(INSCRIPTION_ID_TO_SATPOINT)? - .get(&inscription_id.array())? - .map(|satpoint| Array::from_array(*satpoint.value())), + .get(&inscription_id.store())? + .map(|satpoint| Entry::load(*satpoint.value())), ) } @@ -561,7 +556,7 @@ impl Index { let (start, end) = Index::decode_sat_range(chunk.try_into().unwrap()); if start <= sat && sat < end { return Ok(Some(SatPoint { - outpoint: Array::from_array(*key.value()), + outpoint: Entry::load(*key.value()), offset: offset + sat - start, })); } @@ -572,7 +567,7 @@ impl Index { Ok(None) } - fn list_inner(&self, outpoint: OutPointArray) -> Result>> { + fn list_inner(&self, outpoint: OutPointValue) -> Result>> { Ok( self .database @@ -586,7 +581,7 @@ impl Index { pub(crate) fn list(&self, outpoint: OutPoint) -> Result> { self.require_sat_index("list")?; - let array = outpoint.array(); + let array = outpoint.store(); let sat_ranges = self.list_inner(array)?; @@ -645,12 +640,7 @@ impl Index { .begin_read()? .open_table(SATPOINT_TO_INSCRIPTION_ID)? .range([0; 44]..)? - .map(|(satpoint, id)| { - ( - Array::from_array(*satpoint.value()), - Array::from_array(*id.value()), - ) - }) + .map(|(satpoint, id)| (Entry::load(*satpoint.value()), Entry::load(*id.value()))) .take(n.unwrap_or(usize::MAX)) .collect(), ) @@ -665,7 +655,7 @@ impl Index { .iter()? .rev() .take(n) - .map(|(_number, id)| Array::from_array(*id.value())) + .map(|(_number, id)| Entry::load(*id.value())) .collect(), ) } @@ -679,7 +669,7 @@ impl Index { .database .begin_read()? .open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)? - .get(&inscription_id.array())? + .get(&inscription_id.store())? .map(|value| InscriptionEntry::load(value.value())), ) } @@ -703,9 +693,9 @@ impl Index { ); assert_eq!( - SatPoint::from_array( + SatPoint::load( *inscription_id_to_satpoint - .get(&inscription_id.array()) + .get(&inscription_id.store()) .unwrap() .unwrap() .value() @@ -714,9 +704,9 @@ impl Index { ); assert_eq!( - InscriptionId::from_array( + InscriptionId::load( *satpoint_to_inscription_id - .get(&satpoint.array()) + .get(&satpoint.store()) .unwrap() .unwrap() .value() @@ -726,7 +716,7 @@ impl Index { if self.has_sat_index().unwrap() { assert_eq!( - InscriptionId::from_array( + InscriptionId::load( *rtx .open_table(SAT_TO_INSCRIPTION_ID) .unwrap() @@ -739,7 +729,7 @@ impl Index { ); assert_eq!( - SatPoint::from_array( + SatPoint::load( *rtx .open_table(SAT_TO_SATPOINT) .unwrap() @@ -754,27 +744,26 @@ impl Index { } fn inscriptions_on_output<'a: 'tx, 'tx>( - satpoint_to_id: &'a impl ReadableTable<&'tx SatPointArray, &'tx InscriptionIdArray>, + satpoint_to_id: &'a impl ReadableTable<&'tx SatPointValue, &'tx InscriptionIdValue>, outpoint: OutPoint, ) -> Result + 'tx> { let start = SatPoint { outpoint, offset: 0, } - .array(); + .store(); let end = SatPoint { outpoint, offset: u64::MAX, } - .array(); + .store(); - Ok(satpoint_to_id.range(start..=end)?.map(|(satpoint, id)| { - ( - Array::from_array(*satpoint.value()), - Array::from_array(*id.value()), - ) - })) + Ok( + satpoint_to_id + .range(start..=end)? + .map(|(satpoint, id)| (Entry::load(*satpoint.value()), Entry::load(*id.value()))), + ) } } diff --git a/src/index/array.rs b/src/index/array.rs deleted file mode 100644 index 9ac79920aa..0000000000 --- a/src/index/array.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::*; - -// TODO: -// - rename to entry or something, and reuse for inscription entry -pub(super) trait Array: Sized { - type Array; - - fn from_array(array: Self::Array) -> Self; - - fn array(self) -> Self::Array; -} - -impl Array for InscriptionId { - type Array = InscriptionIdArray; - - fn from_array(array: Self::Array) -> Self { - let (txid, index) = array.split_at(32); - Self { - txid: Txid::from_inner(txid.try_into().unwrap()), - index: u32::from_be_bytes(index.try_into().unwrap()), - } - } - - fn array(self) -> Self::Array { - let mut array = [0; 36]; - let (txid, index) = array.split_at_mut(32); - txid.copy_from_slice(self.txid.as_inner()); - index.copy_from_slice(&self.index.to_be_bytes()); - array - } -} - -impl Array for OutPoint { - type Array = OutPointArray; - - fn from_array(array: Self::Array) -> Self { - Decodable::consensus_decode(&mut io::Cursor::new(array)).unwrap() - } - - fn array(self) -> Self::Array { - let mut array = [0; 36]; - self.consensus_encode(&mut array.as_mut_slice()).unwrap(); - array - } -} - -impl Array for SatPoint { - type Array = SatPointArray; - - fn from_array(array: Self::Array) -> Self { - Decodable::consensus_decode(&mut io::Cursor::new(array)).unwrap() - } - - fn array(self) -> Self::Array { - let mut array = [0; 44]; - self.consensus_encode(&mut array.as_mut_slice()).unwrap(); - array - } -} diff --git a/src/index/inscription_entry.rs b/src/index/inscription_entry.rs deleted file mode 100644 index 670df8b284..0000000000 --- a/src/index/inscription_entry.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::*; - -pub(crate) struct InscriptionEntry { - pub(crate) height: u64, - pub(crate) number: u64, - pub(crate) sat: Option, - pub(crate) timestamp: u32, -} - -pub(crate) type InscriptionEntryValue = (u64, u64, u64, u32); - -impl InscriptionEntry { - pub(crate) fn load((height, number, sat, timestamp): InscriptionEntryValue) -> Self { - Self { - height, - number, - sat: if sat == u64::MAX { - None - } else { - Some(Sat(sat)) - }, - timestamp, - } - } - - pub(crate) fn store(self) -> InscriptionEntryValue { - ( - self.height, - self.number, - match self.sat { - Some(sat) => sat.n(), - None => u64::MAX, - }, - self.timestamp, - ) - } -} diff --git a/src/index/updater.rs b/src/index/updater.rs index 641bf3fa41..24e12e6475 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -24,7 +24,7 @@ impl From for BlockData { } pub(crate) struct Updater { - range_cache: HashMap>, + range_cache: HashMap>, height: u64, index_sats: bool, sat_ranges_since_flush: u64, @@ -321,7 +321,7 @@ impl Updater { let mut input_sat_ranges = VecDeque::new(); for input in &tx.input { - let key = input.previous_output.array(); + let key = input.previous_output.store(); let sat_ranges = match self.range_cache.remove(&key) { Some(sat_ranges) => { @@ -374,7 +374,7 @@ impl Updater { outpoint: OutPoint::null(), offset: lost_sats, } - .array(), + .store(), )?; } @@ -389,10 +389,7 @@ impl Updater { statistic_to_count.insert(&Statistic::LostSats.key(), &lost_sats)?; - height_to_block_hash.insert( - &self.height, - &block.header.block_hash().as_hash().into_inner(), - )?; + height_to_block_hash.insert(&self.height, &block.header.block_hash().store())?; self.height += 1; self.outputs_traversed += outputs_in_block; @@ -409,7 +406,7 @@ impl Updater { &mut self, tx: &Transaction, txid: Txid, - sat_to_satpoint: &mut Table, + sat_to_satpoint: &mut Table, input_sat_ranges: &mut VecDeque<(u64, u64)>, sat_ranges_written: &mut u64, outputs_traversed: &mut u64, @@ -437,7 +434,7 @@ impl Updater { outpoint, offset: output.value - remaining, } - .array(), + .store(), )?; } @@ -466,7 +463,7 @@ impl Updater { *outputs_traversed += 1; - self.range_cache.insert(outpoint.array(), sats); + self.range_cache.insert(outpoint.store(), sats); self.outputs_inserted_since_flush += 1; } @@ -503,7 +500,7 @@ impl Updater { let mut outpoint_to_value = wtx.open_table(OUTPOINT_TO_VALUE)?; for (outpoint, value) in value_cache { - outpoint_to_value.insert(&outpoint.array(), &value)?; + outpoint_to_value.insert(&outpoint.store(), &value)?; } } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 74258520ca..eed2d903b8 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -14,16 +14,16 @@ enum Origin { pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { flotsam: Vec, height: u64, - id_to_satpoint: &'a mut Table<'db, 'tx, &'tx InscriptionIdArray, &'tx SatPointArray>, + id_to_satpoint: &'a mut Table<'db, 'tx, &'tx InscriptionIdValue, &'tx SatPointValue>, index: &'a Index, - id_to_entry: &'a mut Table<'db, 'tx, &'tx InscriptionIdArray, InscriptionEntryValue>, + id_to_entry: &'a mut Table<'db, 'tx, &'tx InscriptionIdValue, InscriptionEntryValue>, lost_sats: u64, next_number: u64, - number_to_id: &'a mut Table<'db, 'tx, u64, &'tx InscriptionIdArray>, - outpoint_to_value: &'a mut Table<'db, 'tx, &'tx OutPointArray, u64>, + number_to_id: &'a mut Table<'db, 'tx, u64, &'tx InscriptionIdValue>, + outpoint_to_value: &'a mut Table<'db, 'tx, &'tx OutPointValue, u64>, reward: u64, - sat_to_inscription_id: &'a mut Table<'db, 'tx, u64, &'tx InscriptionIdArray>, - satpoint_to_id: &'a mut Table<'db, 'tx, &'tx SatPointArray, &'tx InscriptionIdArray>, + sat_to_inscription_id: &'a mut Table<'db, 'tx, u64, &'tx InscriptionIdValue>, + satpoint_to_id: &'a mut Table<'db, 'tx, &'tx SatPointValue, &'tx InscriptionIdValue>, timestamp: u32, value_cache: &'a mut HashMap, } @@ -31,14 +31,14 @@ pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { pub(super) fn new( height: u64, - id_to_satpoint: &'a mut Table<'db, 'tx, &'tx InscriptionIdArray, &'tx SatPointArray>, + id_to_satpoint: &'a mut Table<'db, 'tx, &'tx InscriptionIdValue, &'tx SatPointValue>, index: &'a Index, - id_to_entry: &'a mut Table<'db, 'tx, &'tx InscriptionIdArray, InscriptionEntryValue>, + id_to_entry: &'a mut Table<'db, 'tx, &'tx InscriptionIdValue, InscriptionEntryValue>, lost_sats: u64, - number_to_id: &'a mut Table<'db, 'tx, u64, &'tx InscriptionIdArray>, - outpoint_to_value: &'a mut Table<'db, 'tx, &'tx OutPointArray, u64>, - sat_to_inscription_id: &'a mut Table<'db, 'tx, u64, &'tx InscriptionIdArray>, - satpoint_to_id: &'a mut Table<'db, 'tx, &'tx SatPointArray, &'tx InscriptionIdArray>, + number_to_id: &'a mut Table<'db, 'tx, u64, &'tx InscriptionIdValue>, + outpoint_to_value: &'a mut Table<'db, 'tx, &'tx OutPointValue, u64>, + sat_to_inscription_id: &'a mut Table<'db, 'tx, u64, &'tx InscriptionIdValue>, + satpoint_to_id: &'a mut Table<'db, 'tx, &'tx SatPointValue, &'tx InscriptionIdValue>, timestamp: u32, value_cache: &'a mut HashMap, ) -> Result { @@ -102,7 +102,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { value } else if let Some(value) = self .outpoint_to_value - .remove(&tx_in.previous_output.array())? + .remove(&tx_in.previous_output.store())? { value.value() } else { @@ -195,11 +195,11 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { flotsam: Flotsam, new_satpoint: SatPoint, ) -> Result { - let inscription_id = flotsam.inscription_id.array(); + let inscription_id = flotsam.inscription_id.store(); match flotsam.origin { Origin::Old(old_satpoint) => { - self.satpoint_to_id.remove(&old_satpoint.array())?; + self.satpoint_to_id.remove(&old_satpoint.store())?; } Origin::New => { self @@ -236,7 +236,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } - let new_satpoint = new_satpoint.array(); + let new_satpoint = new_satpoint.store(); self.satpoint_to_id.insert(&new_satpoint, &inscription_id)?; self.id_to_satpoint.insert(&inscription_id, &new_satpoint)?; From 8a99f013a45a882985d05bffe926bb11f2e5fa9f Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 12:03:05 -0800 Subject: [PATCH 18/22] FIx --- src/index/entry.rs | 117 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/index/entry.rs diff --git a/src/index/entry.rs b/src/index/entry.rs new file mode 100644 index 0000000000..6361e216cc --- /dev/null +++ b/src/index/entry.rs @@ -0,0 +1,117 @@ +use super::*; + +pub(super) type SatRangeValue = [u8; 11]; + +pub(super) trait Entry: Sized { + type Value; + + fn load(value: Self::Value) -> Self; + + fn store(self) -> Self::Value; +} + +pub(super) type InscriptionIdValue = [u8; 36]; + +impl Entry for InscriptionId { + type Value = InscriptionIdValue; + + fn load(value: Self::Value) -> Self { + let (txid, index) = value.split_at(32); + Self { + txid: Txid::from_inner(txid.try_into().unwrap()), + index: u32::from_be_bytes(index.try_into().unwrap()), + } + } + + fn store(self) -> Self::Value { + let mut value = [0; 36]; + let (txid, index) = value.split_at_mut(32); + txid.copy_from_slice(self.txid.as_inner()); + index.copy_from_slice(&self.index.to_be_bytes()); + value + } +} + +pub(super) type OutPointValue = [u8; 36]; + +impl Entry for OutPoint { + type Value = OutPointValue; + + fn load(value: Self::Value) -> Self { + Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() + } + + fn store(self) -> Self::Value { + let mut value = [0; 36]; + self.consensus_encode(&mut value.as_mut_slice()).unwrap(); + value + } +} + +pub(super) type SatPointValue = [u8; 44]; + +impl Entry for SatPoint { + type Value = SatPointValue; + + fn load(value: Self::Value) -> Self { + Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() + } + + fn store(self) -> Self::Value { + let mut value = [0; 44]; + self.consensus_encode(&mut value.as_mut_slice()).unwrap(); + value + } +} + +pub(crate) struct InscriptionEntry { + pub(crate) height: u64, + pub(crate) number: u64, + pub(crate) sat: Option, + pub(crate) timestamp: u32, +} + +pub(crate) type InscriptionEntryValue = (u64, u64, u64, u32); + +impl Entry for InscriptionEntry { + type Value = InscriptionEntryValue; + + fn load((height, number, sat, timestamp): InscriptionEntryValue) -> Self { + Self { + height, + number, + sat: if sat == u64::MAX { + None + } else { + Some(Sat(sat)) + }, + timestamp, + } + } + + fn store(self) -> Self::Value { + ( + self.height, + self.number, + match self.sat { + Some(sat) => sat.n(), + None => u64::MAX, + }, + self.timestamp, + ) + } +} + +pub(super) type BlockHashValue = [u8; 32]; + +impl Entry for BlockHash { + type Value = BlockHashValue; + + fn load(value: Self::Value) -> Self { + BlockHash::from_inner(value) + } + + fn store(self) -> Self::Value { + self.into_inner() + } +} From f5ee0cdeda0605bd8c33b63d12bd349a376e1552 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 12:05:25 -0800 Subject: [PATCH 19/22] Sort --- src/index/entry.rs | 104 ++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/index/entry.rs b/src/index/entry.rs index 6361e216cc..265496c73a 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -1,7 +1,5 @@ use super::*; -pub(super) type SatRangeValue = [u8; 11]; - pub(super) trait Entry: Sized { type Value; @@ -10,57 +8,17 @@ pub(super) trait Entry: Sized { fn store(self) -> Self::Value; } -pub(super) type InscriptionIdValue = [u8; 36]; - -impl Entry for InscriptionId { - type Value = InscriptionIdValue; - - fn load(value: Self::Value) -> Self { - let (txid, index) = value.split_at(32); - Self { - txid: Txid::from_inner(txid.try_into().unwrap()), - index: u32::from_be_bytes(index.try_into().unwrap()), - } - } - - fn store(self) -> Self::Value { - let mut value = [0; 36]; - let (txid, index) = value.split_at_mut(32); - txid.copy_from_slice(self.txid.as_inner()); - index.copy_from_slice(&self.index.to_be_bytes()); - value - } -} - -pub(super) type OutPointValue = [u8; 36]; - -impl Entry for OutPoint { - type Value = OutPointValue; - - fn load(value: Self::Value) -> Self { - Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() - } - - fn store(self) -> Self::Value { - let mut value = [0; 36]; - self.consensus_encode(&mut value.as_mut_slice()).unwrap(); - value - } -} - -pub(super) type SatPointValue = [u8; 44]; +pub(super) type BlockHashValue = [u8; 32]; -impl Entry for SatPoint { - type Value = SatPointValue; +impl Entry for BlockHash { + type Value = BlockHashValue; fn load(value: Self::Value) -> Self { - Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() + BlockHash::from_inner(value) } fn store(self) -> Self::Value { - let mut value = [0; 44]; - self.consensus_encode(&mut value.as_mut_slice()).unwrap(); - value + self.into_inner() } } @@ -102,16 +60,58 @@ impl Entry for InscriptionEntry { } } -pub(super) type BlockHashValue = [u8; 32]; +pub(super) type InscriptionIdValue = [u8; 36]; -impl Entry for BlockHash { - type Value = BlockHashValue; +impl Entry for InscriptionId { + type Value = InscriptionIdValue; fn load(value: Self::Value) -> Self { - BlockHash::from_inner(value) + let (txid, index) = value.split_at(32); + Self { + txid: Txid::from_inner(txid.try_into().unwrap()), + index: u32::from_be_bytes(index.try_into().unwrap()), + } } fn store(self) -> Self::Value { - self.into_inner() + let mut value = [0; 36]; + let (txid, index) = value.split_at_mut(32); + txid.copy_from_slice(self.txid.as_inner()); + index.copy_from_slice(&self.index.to_be_bytes()); + value + } +} + +pub(super) type OutPointValue = [u8; 36]; + +impl Entry for OutPoint { + type Value = OutPointValue; + + fn load(value: Self::Value) -> Self { + Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() + } + + fn store(self) -> Self::Value { + let mut value = [0; 36]; + self.consensus_encode(&mut value.as_mut_slice()).unwrap(); + value + } +} + +pub(super) type SatPointValue = [u8; 44]; + +impl Entry for SatPoint { + type Value = SatPointValue; + + fn load(value: Self::Value) -> Self { + Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() + } + + fn store(self) -> Self::Value { + let mut value = [0; 44]; + self.consensus_encode(&mut value.as_mut_slice()).unwrap(); + value } } + +pub(super) type SatRangeValue = [u8; 11]; From cf1a16c913998c34b244931503f1c414f125035f Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 12:16:03 -0800 Subject: [PATCH 20/22] Add tests --- src/inscription_id.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/test.rs | 13 +++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/inscription_id.rs b/src/inscription_id.rs index 40ae868f0e..ca9dc263e6 100644 --- a/src/inscription_id.rs +++ b/src/inscription_id.rs @@ -137,4 +137,42 @@ mod tests { }, ); } + + #[test] + fn from_str_bad_character() { + assert_matches!( + "→".parse::(), + Err(ParseError::Character('→')), + ); + } + + #[test] + fn from_str_bad_length() { + assert_matches!("foo".parse::(), Err(ParseError::Length(3))); + } + + #[test] + fn from_str_bad_separator() { + assert_matches!( + "0000000000000000000000000000000000000000000000000000000000000000x0".parse::(), + Err(ParseError::Separator('x')), + ); + } + + #[test] + fn from_str_bad_index() { + assert_matches!( + "0000000000000000000000000000000000000000000000000000000000000000ifoo" + .parse::(), + Err(ParseError::Index(_)), + ); + } + + #[test] + fn from_str_bad_txid() { + assert_matches!( + "x000000000000000000000000000000000000000000000000000000000000000i0".parse::(), + Err(ParseError::Txid(_)), + ); + } } diff --git a/src/test.rs b/src/test.rs index 960043d2eb..82ffc78bed 100644 --- a/src/test.rs +++ b/src/test.rs @@ -17,6 +17,19 @@ macro_rules! assert_regex_match { }; } +macro_rules! assert_matches { + ($expression:expr, $( $pattern:pat_param )|+ $( if $guard:expr )? $(,)?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => {} + left => panic!( + "assertion failed: (left ~= right)\n left: `{:?}`\n right: `{}`", + left, + stringify!($($pattern)|+ $(if $guard)?) + ), + } + } +} + pub(crate) fn txid(n: u64) -> Txid { let hex = format!("{n:x}"); From 59def476d912ea92f1511a173b1544410b4fb3ea Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 12:21:27 -0800 Subject: [PATCH 21/22] Use BlockHash::load --- src/index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.rs b/src/index.rs index b4d35d3db1..d9813d2776 100644 --- a/src/index.rs +++ b/src/index.rs @@ -387,7 +387,7 @@ impl Index { let height_to_block_hash = rtx.0.open_table(HEIGHT_TO_BLOCK_HASH)?; for next in height_to_block_hash.range(0..block_count)?.rev().take(take) { - blocks.push((next.0.value(), BlockHash::load(*next.1.value()))); + blocks.push((next.0.value(), Entry::load(*next.1.value()))); } Ok(blocks) From 97a9d8684a4f1f0f9bc838bdad8b247ac2e7fe73 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 12 Jan 2023 12:39:17 -0800 Subject: [PATCH 22/22] tweak --- src/subcommand/wallet/transaction_builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subcommand/wallet/transaction_builder.rs b/src/subcommand/wallet/transaction_builder.rs index f5261515e7..bba092c0dc 100644 --- a/src/subcommand/wallet/transaction_builder.rs +++ b/src/subcommand/wallet/transaction_builder.rs @@ -1051,7 +1051,7 @@ mod tests { pretty_assert_eq!( TransactionBuilder::build_transaction( satpoint(1, 0), - BTreeMap::from([(satpoint(2, 10 * COIN_VALUE), inscription_id(1),)]), + BTreeMap::from([(satpoint(2, 10 * COIN_VALUE), inscription_id(1))]), utxos.into_iter().collect(), recipient(), vec![change(0), change(1)],