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 381cc1f479..d9813d2776 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1,6 +1,9 @@ use { self::{ - inscription_entry::{InscriptionEntry, InscriptionEntryValue}, + entry::{ + BlockHashValue, Entry, InscriptionEntry, InscriptionEntryValue, InscriptionIdValue, + OutPointValue, SatPointValue, SatRangeValue, + }, updater::Updater, }, super::*, @@ -13,16 +16,10 @@ use { std::sync::atomic::{self, AtomicBool}, }; -mod inscription_entry; +mod entry; mod rtx; mod updater; -type BlockHashArray = [u8; 32]; -type InscriptionIdArray = [u8; 32]; -type OutPointArray = [u8; 36]; -type SatPointArray = [u8; 44]; -type SatRangeArray = [u8; 11]; - const SCHEMA_VERSION: u64 = 1; macro_rules! define_table { @@ -31,46 +28,18 @@ 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 } -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, @@ -336,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, ]); @@ -418,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(), Entry::load(*next.1.value()))); } Ok(blocks) @@ -433,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()), decode_satpoint(*satpoint.value()))); + result.push((Sat(sat.value()), Entry::load(*satpoint.value()))); } Ok(Some(result)) @@ -450,7 +419,7 @@ impl Index { .begin_read()? .open_table(SAT_TO_SATPOINT)? .get(&sat.n())? - .map(|satpoint| decode_satpoint(*satpoint.value())), + .map(|satpoint| Entry::load(*satpoint.value())), ) } else { Ok(None) @@ -487,7 +456,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| Entry::load(*inscription_id.value())), ) } @@ -501,7 +470,7 @@ impl Index { .begin_read()? .open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)? .get(&n)? - .map(|id| decode_inscription_id(*id.value())), + .map(|id| Entry::load(*id.value())), ) } @@ -514,8 +483,8 @@ 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.store())? + .map(|satpoint| Entry::load(*satpoint.value())), ) } @@ -525,7 +494,7 @@ impl Index { ) -> Result> { Ok( self - .get_transaction(inscription_id)? + .get_transaction(inscription_id.txid)? .and_then(|tx| Inscription::from_transaction(&tx)), ) } @@ -586,9 +555,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: Entry::load(*key.value()), offset: offset + sat - start, })); } @@ -599,7 +567,7 @@ impl Index { Ok(None) } - fn list_inner(&self, outpoint: OutPointArray) -> Result>> { + fn list_inner(&self, outpoint: OutPointValue) -> Result>> { Ok( self .database @@ -613,9 +581,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.store(); - 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( @@ -672,12 +640,7 @@ impl Index { .begin_read()? .open_table(SATPOINT_TO_INSCRIPTION_ID)? .range([0; 44]..)? - .map(|(satpoint, id)| { - ( - decode_satpoint(*satpoint.value()), - decode_inscription_id(*id.value()), - ) - }) + .map(|(satpoint, id)| (Entry::load(*satpoint.value()), Entry::load(*id.value()))) .take(n.unwrap_or(usize::MAX)) .collect(), ) @@ -692,7 +655,7 @@ impl Index { .iter()? .rev() .take(n) - .map(|(_number, id)| decode_inscription_id(*id.value())) + .map(|(_number, id)| Entry::load(*id.value())) .collect(), ) } @@ -706,7 +669,7 @@ impl Index { .database .begin_read()? .open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)? - .get(inscription_id.as_inner())? + .get(&inscription_id.store())? .map(|value| InscriptionEntry::load(value.value())), ) } @@ -730,9 +693,9 @@ impl Index { ); assert_eq!( - decode_satpoint( + SatPoint::load( *inscription_id_to_satpoint - .get(&inscription_id.as_inner()) + .get(&inscription_id.store()) .unwrap() .unwrap() .value() @@ -741,9 +704,9 @@ impl Index { ); assert_eq!( - InscriptionId::from_inner( + InscriptionId::load( *satpoint_to_inscription_id - .get(&encode_satpoint(satpoint)) + .get(&satpoint.store()) .unwrap() .unwrap() .value() @@ -753,7 +716,7 @@ impl Index { if self.has_sat_index().unwrap() { assert_eq!( - InscriptionId::from_inner( + InscriptionId::load( *rtx .open_table(SAT_TO_INSCRIPTION_ID) .unwrap() @@ -766,7 +729,7 @@ impl Index { ); assert_eq!( - decode_satpoint( + SatPoint::load( *rtx .open_table(SAT_TO_SATPOINT) .unwrap() @@ -781,25 +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 = encode_satpoint(SatPoint { + let start = SatPoint { outpoint, offset: 0, - }); + } + .store(); - let end = encode_satpoint(SatPoint { + let end = SatPoint { outpoint, offset: u64::MAX, - }); + } + .store(); - Ok(satpoint_to_id.range(start..=end)?.map(|(satpoint, id)| { - ( - decode_satpoint(*satpoint.value()), - InscriptionId::from_inner(*id.value()), - ) - })) + Ok( + satpoint_to_id + .range(start..=end)? + .map(|(satpoint, id)| (Entry::load(*satpoint.value()), Entry::load(*id.value()))), + ) } } @@ -933,7 +897,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!( @@ -947,10 +912,7 @@ mod tests { .get_inscription_satpoint_by_id(inscription_id) .unwrap(), Some(SatPoint { - outpoint: OutPoint { - txid: inscription_id, - vout: 0, - }, + outpoint: OutPoint { txid, vout: 0 }, offset: 0, }) ); @@ -961,7 +923,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!( @@ -1271,21 +1234,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, @@ -1298,21 +1259,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, @@ -1344,17 +1303,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); @@ -1396,21 +1358,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, @@ -1447,21 +1407,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, @@ -1493,11 +1451,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); @@ -1528,12 +1487,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(); @@ -1556,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); context.mine_blocks_with_subsidy(1, 0); @@ -1581,22 +1542,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); @@ -1653,12 +1616,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 { @@ -1684,22 +1648,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, @@ -1712,12 +1674,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( @@ -1821,19 +1784,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(), [] ); @@ -1843,10 +1805,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] ); @@ -1861,10 +1820,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/entry.rs b/src/index/entry.rs new file mode 100644 index 0000000000..265496c73a --- /dev/null +++ b/src/index/entry.rs @@ -0,0 +1,117 @@ +use super::*; + +pub(super) trait Entry: Sized { + type Value; + + fn load(value: Self::Value) -> Self; + + fn store(self) -> Self::Value; +} + +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() + } +} + +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 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(super) type SatRangeValue = [u8; 11]; 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 f960e70cbe..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 = encode_outpoint(input.previous_output); + let key = input.previous_output.store(); let sat_ranges = match self.range_cache.remove(&key) { Some(sat_ranges) => { @@ -370,10 +370,11 @@ impl Updater { if !Sat(start).is_common() { sat_to_satpoint.insert( &start, - &encode_satpoint(SatPoint { + &SatPoint { outpoint: OutPoint::null(), offset: lost_sats, - }), + } + .store(), )?; } @@ -388,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; @@ -408,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, @@ -432,10 +430,11 @@ impl Updater { if !Sat(range.0).is_common() { sat_to_satpoint.insert( &range.0, - &encode_satpoint(SatPoint { + &SatPoint { outpoint, offset: output.value - remaining, - }), + } + .store(), )?; } @@ -464,7 +463,7 @@ impl Updater { *outputs_traversed += 1; - self.range_cache.insert(encode_outpoint(outpoint), sats); + self.range_cache.insert(outpoint.store(), sats); self.outputs_inserted_since_flush += 1; } @@ -501,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(&encode_outpoint(outpoint), &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 5255aac604..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 { @@ -77,7 +77,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, }); @@ -102,7 +102,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.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.into_inner(); + let inscription_id = flotsam.inscription_id.store(); match flotsam.origin { Origin::Old(old_satpoint) => { - self.satpoint_to_id.remove(&encode_satpoint(old_satpoint))?; + 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 = encode_satpoint(new_satpoint); + let new_satpoint = new_satpoint.store(); 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..ca9dc263e6 --- /dev/null +++ b/src/inscription_id.rs @@ -0,0 +1,178 @@ +use super::*; + +#[derive(Debug, PartialEq, Copy, Clone)] +pub(crate) struct InscriptionId { + pub(crate) txid: Txid, + pub(crate) index: 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.index) + } +} + +#[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 = ParseError; + + 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 { + return Err(ParseError::Length(s.len())); + } + + let txid = &s[..TXID_LEN]; + + 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().map_err(ParseError::Txid)?, + index: vout.parse().map_err(ParseError::Index)?, + }) + } +} + +impl From for InscriptionId { + fn from(txid: Txid) -> Self { + Self { txid, index: 0 } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn display() { + assert_eq!( + inscription_id(1).to_string(), + "1111111111111111111111111111111111111111111111111111111111111111i1", + ); + assert_eq!( + InscriptionId { + txid: txid(1), + index: 0, + } + .to_string(), + "1111111111111111111111111111111111111111111111111111111111111111i0", + ); + assert_eq!( + InscriptionId { + txid: txid(1), + index: 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), + index: 0xFFFFFFFF, + }, + ); + assert_eq!( + "1111111111111111111111111111111111111111111111111111111111111111i4294967295" + .parse::() + .unwrap(), + InscriptionId { + txid: txid(1), + index: 0xFFFFFFFF, + }, + ); + } + + #[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/main.rs b/src/main.rs index 6a7af69afa..016031854c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,10 +17,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, outgoing::Outgoing, @@ -47,7 +49,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}, @@ -84,11 +86,13 @@ mod chain; mod content; mod decimal; mod degree; +mod deserialize_from_str; mod epoch; mod fee_rate; mod height; mod index; mod inscription; +mod inscription_id; mod object; mod options; mod outgoing; @@ -102,8 +106,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/object.rs b/src/object.rs index 4c5e07e87a..a967caac98 100644 --- a/src/object.rs +++ b/src/object.rs @@ -4,6 +4,7 @@ use super::*; pub(crate) enum Object { Address(Address), Hash([u8; 32]), + InscriptionId(InscriptionId), Integer(u128), OutPoint(OutPoint), Sat(Sat), @@ -22,6 +23,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()?)), OutPoint => Ok(Self::OutPoint(s.parse()?)), SatPoint => Ok(Self::SatPoint(s.parse()?)), @@ -39,6 +41,7 @@ impl Display for Object { } Ok(()) } + Self::InscriptionId(inscription_id) => write!(f, "{inscription_id}"), Self::Integer(integer) => write!(f, "{integer}"), Self::OutPoint(outpoint) => write!(f, "{}", outpoint), Self::Sat(sat) => write!(f, "{sat}"), @@ -78,6 +81,15 @@ mod tests { case("0", Object::Integer(0)); + case( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefi1", + Object::InscriptionId( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefi1" + .parse() + .unwrap(), + ), + ); + case( "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", Object::Hash([ 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() ), diff --git a/src/representation.rs b/src/representation.rs index a77c0d9499..0f80680dc8 100644 --- a/src/representation.rs +++ b/src/representation.rs @@ -6,6 +6,7 @@ pub(crate) enum Representation { Decimal, Degree, Hash, + InscriptionId, Integer, Name, OutPoint, @@ -22,6 +23,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::OutPoint => r"^[[:xdigit:]]{64}:\d+$", @@ -49,6 +51,7 @@ const PATTERNS: &[(Representation, &str)] = &[ Representation::Decimal.pattern(), Representation::Degree.pattern(), Representation::Hash.pattern(), + Representation::InscriptionId.pattern(), Representation::Integer.pattern(), Representation::Name.pattern(), Representation::OutPoint.pattern(), diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 228c26f337..029cdf5d52 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -25,13 +25,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 { @@ -469,14 +467,14 @@ impl Server { Extension(chain): Extension, Path(txid): Path, ) -> ServerResult> { - let inscription = index.get_inscription_by_id(txid)?; + let inscription = index.get_inscription_by_id(txid.into())?; Ok( TransactionHtml::new( index .get_transaction(txid)? .ok_or_not_found(|| format!("transaction {txid}"))?, - inscription, + inscription.map(|_| txid.into()), chain, ) .page(chain, index.has_sat_index()?), @@ -1722,7 +1720,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() @@ -1731,7 +1729,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
.*", @@ -1743,7 +1741,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", @@ -1756,7 +1754,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>
.*", @@ -1768,11 +1766,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); @@ -1789,7 +1788,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() @@ -1798,7 +1797,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", @@ -1810,7 +1809,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() @@ -1819,7 +1818,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(), @@ -1831,7 +1830,7 @@ mod tests { let server = TestServer::new_with_sat_index(); 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() @@ -1840,7 +1839,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
.*", ); @@ -1851,7 +1850,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() @@ -1860,7 +1859,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/inscribe.rs b/src/subcommand/wallet/inscribe.rs index bd40f97d23..fb01fa97f7 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -385,7 +385,7 @@ mod tests { outpoint: outpoint(1), offset: 0, }, - Txid::from_str("06413a3ef4232f0485df2bc7c912c13c05c69f967c19639344753e05edb64bd5").unwrap(), + inscription_id(1), ); let inscription = inscription("text/plain", "ord"); @@ -425,7 +425,7 @@ mod tests { outpoint: outpoint(1), offset: 0, }, - Txid::from_str("06413a3ef4232f0485df2bc7c912c13c05c69f967c19639344753e05edb64bd5").unwrap(), + inscription_id(1), ); let inscription = inscription("text/plain", "ord"); @@ -458,7 +458,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/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 4a40e18d6f..c897b5dc5d 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -40,9 +40,9 @@ impl Send { } satpoint } - Outgoing::InscriptionId(txid) => index - .get_inscription_satpoint_by_id(txid)? - .ok_or_else(|| anyhow!("No inscription found for {txid}"))?, + Outgoing::InscriptionId(id) => index + .get_inscription_satpoint_by_id(id)? + .ok_or_else(|| anyhow!("Inscription {id} not found"))?, Outgoing::Amount(amount) => { let all_inscription_outputs = inscriptions .keys() diff --git a/src/subcommand/wallet/transaction_builder.rs b/src/subcommand/wallet/transaction_builder.rs index c4842f82f4..bba092c0dc 100644 --- a/src/subcommand/wallet/transaction_builder.rs +++ b/src/subcommand/wallet/transaction_builder.rs @@ -1051,12 +1051,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)], @@ -1073,12 +1068,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)], @@ -1087,9 +1077,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), }) ) } @@ -1102,12 +1090,7 @@ mod tests { let transaction = TransactionBuilder::build_transaction( satpoint(1, 0), - BTreeMap::from([( - satpoint(1, 0), - "bed200b55adcf20e359bbb762392d5106cafbafc48e55f77c94d3041de3521da" - .parse() - .unwrap(), - )]), + BTreeMap::from([(satpoint(1, 0), inscription_id(1))]), utxos.into_iter().collect(), recipient(), vec![change(0), change(1)], diff --git a/src/templates/home.rs b/src/templates/home.rs index e8f0106b66..436d295b87 100644 --- a/src/templates/home.rs +++ b/src/templates/home.rs @@ -49,13 +49,13 @@ mod tests { .unwrap() ) ], - vec![txid(1), txid(2),], + vec![inscription_id(1), inscription_id(2)], ) .to_string(), "

Latest Inscriptions

- - + +

Latest Blocks

diff --git a/src/templates/iframe.rs b/src/templates/iframe.rs index d800b90850..7647d7e2a6 100644 --- a/src/templates/iframe.rs +++ b/src/templates/iframe.rs @@ -48,18 +48,18 @@ mod tests { #[test] fn preview() { assert_regex_match!( - Iframe::thumbnail(txid(1)) + Iframe::thumbnail(inscription_id(1)) .0.to_string(), - "", + "", ); } #[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 19cb843e06..bf4098133f 100644 --- a/src/templates/inscription.rs +++ b/src/templates/inscription.rs @@ -36,7 +36,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()), @@ -49,18 +49,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
@@ -90,7 +90,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()), @@ -121,11 +121,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), timestamp: timestamp(0), @@ -133,9 +133,9 @@ mod tests { "

Inscription 1

- - - + + +
.* " diff --git a/src/templates/inscriptions.rs b/src/templates/inscriptions.rs index ecd6524558..09b5e57b1c 100644 --- a/src/templates/inscriptions.rs +++ b/src/templates/inscriptions.rs @@ -19,13 +19,13 @@ mod tests { fn inscriptions() { assert_regex_match!( InscriptionsHtml { - inscriptions: vec![txid(1), txid(2)], + inscriptions: vec![inscription_id(1), inscription_id(2)], }, "

Inscriptions

- - + +
" .unindent() diff --git a/src/templates/output.rs b/src/templates/output.rs index 6d68194dd7..e3de7e991a 100644 --- a/src/templates/output.rs +++ b/src/templates/output.rs @@ -115,10 +115,8 @@ mod tests { fn with_inscriptions() { assert_regex_match!( OutputHtml { - inscriptions: vec![txid(1)], - outpoint: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0" - .parse() - .unwrap(), + inscriptions: vec![inscription_id(1)], + outpoint: outpoint(1), list: None, chain: Chain::Mainnet, output: TxOut { @@ -127,15 +125,13 @@ mod tests { }, }, " -

Output 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0

+

Output 1{64}:1

inscriptions
- +
-
value
3
-
script pubkey
OP_DUP OP_HASH160 OP_PUSHBYTES_20 0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG
-
address
1111111111111111111114oLvT2
+ .*
" .unindent() diff --git a/src/templates/sat.rs b/src/templates/sat.rs index 0d4001df43..a40a086259 100644 --- a/src/templates/sat.rs +++ b/src/templates/sat.rs @@ -102,9 +102,9 @@ mod tests { sat: Sat(0), satpoint: None, blocktime: Blocktime::Confirmed(0), - inscription: Some(txid(1)), + inscription: Some(inscription_id(1)), }, - r"

Sat 0

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

Sat 0

.*
inscription
.*
.*", ); } 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..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}"); @@ -80,3 +93,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/inscription.html b/templates/inscription.html index 3a30fee049..fa6249f06c 100644 --- a/templates/inscription.html +++ b/templates/inscription.html @@ -40,7 +40,7 @@

Inscription {{ self.number }}

genesis height
{{ self.genesis_height }}
genesis transaction
-
{{ self.inscription_id }}
+
{{ self.inscription_id.txid }}
location
{{ self.satpoint }}
output
diff --git a/templates/transaction.html b/templates/transaction.html index f4e3fe6888..7bc578a29b 100644 --- a/templates/transaction.html +++ b/templates/transaction.html @@ -1,8 +1,8 @@

Transaction {{self.txid}}

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

Inscription Geneses

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

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

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 b2096fe455..d71bedcb42 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -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
@@ -142,18 +146,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 bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {reveal_txid}" + "wallet send bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {inscription_id}" )) .rpc_server(&rpc_server) .stdout_regex(".*") @@ -165,7 +171,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
.*", ), @@ -177,20 +183,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!( @@ -249,24 +249,14 @@ 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); - - rpc_server.mine_blocks(1); + let inscription_id = create_inscription(&rpc_server, "foo.png"); TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( "/inscriptions", format!( ".*

Inscriptions

- .* + .*
.*", ), diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index c0634d44f6..1e5eb455ed 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -15,7 +15,9 @@ fn inscribe_creates_inscription_transactions() { .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"); assert_eq!(rpc_server.descriptors().len(), 3); @@ -35,24 +37,18 @@ fn inscribe_creates_inscription_transactions() { #[test] fn inscribe_with_satpoint_arg_inscribes_specific_satpoint() { 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}")); @@ -212,8 +208,8 @@ fn refuse_to_reinscribe_sats() { fn refuse_to_inscribe_already_inscribed_utxo() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let stdout = CommandBuilder::new("wallet inscribe degenerate.png") .write("degenerate.png", [1; 100]) .rpc_server(&rpc_server) @@ -222,7 +218,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), @@ -236,7 +232,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(); } @@ -245,15 +241,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 6265445fe2..b81503b1fc 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -4,21 +4,22 @@ 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 reveal_txid = reveal_txid_from_inscribe_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") .run(), ); - rpc_server.mine_blocks(1); + let inscription_id = format!("{reveal_txid}i0"); + CommandBuilder::new("wallet inscriptions") .rpc_server(&rpc_server) - .expected_stdout(format!("{inscription_id}\t{inscription_id}:0:0\n")) + .expected_stdout(format!("{inscription_id}\t{reveal_txid}:0:0\n")) .run(); let stdout = CommandBuilder::new("wallet receive") @@ -54,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) @@ -64,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 561c1e7163..7878e4d741 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -4,21 +4,14 @@ use super::*; fn inscriptions_can_be_sent() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - rpc_server.mine_blocks(1); - let stdout = CommandBuilder::new("--index-sats wallet inscribe 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 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {reveal_txid}" + "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription_id}" )) .rpc_server(&rpc_server) .stdout_regex(r".*") @@ -33,13 +26,13 @@ fn inscriptions_can_be_sent() { 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
@@ -58,10 +51,10 @@ fn send_unknown_inscription() { let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new(format!( - "wallet send bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {txid}" + "wallet send bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {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(); } @@ -70,22 +63,14 @@ fn send_unknown_inscription() { fn send_inscribed_sat() { 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!( - "--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!( - "wallet send bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {reveal_txid}" + "wallet send bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {inscription_id}" )) .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\n") @@ -97,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
.*", ), @@ -209,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); @@ -224,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(); } @@ -349,7 +334,7 @@ fn wallet_send_with_fee_rate() { let reveal_txid = reveal_txid_from_inscribe_stdout(&stdout); CommandBuilder::new(format!( - "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {reveal_txid} --fee-rate 2.0" + "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {reveal_txid}i0 --fee-rate 2.0" )) .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\n")