From 35c43ccc82432bb2cc18c5b3e14ef764b7b93242 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 21 Jun 2023 10:30:02 +0200 Subject: [PATCH 01/14] add test --- src/index.rs | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/src/index.rs b/src/index.rs index cb69e83032..c65981955c 100644 --- a/src/index.rs +++ b/src/index.rs @@ -2782,4 +2782,124 @@ mod tests { ); } } + + #[test] + fn second_reinscription_on_cursed_inscription_is_cursed_and_unbound() { + for context in Context::configurations() { + context.mine_blocks(1); + context.mine_blocks(1); + + let witness = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); + + let cursed_txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0), (2, 0, 0)], + witness, + outputs: 2, + ..Default::default() + }); + + let cursed = InscriptionId { + txid: cursed_txid, + index: 1, + }; + + context.mine_blocks(1); + + context.index.assert_inscription_location( + cursed, + SatPoint { + outpoint: OutPoint { + txid: cursed_txid, + vout: 1, + }, + offset: 0, + }, + Some(100 * COIN_VALUE), + ); + + assert_eq!( + context + .index + .get_inscription_entry(cursed) + .unwrap() + .unwrap() + .number, + -1 + ); + + let witness = envelope(&[ + b"ord", + &[1], + b"text/plain;charset=utf-8", + &[], + b"reinscription on cursed", + ]); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(3, 1, 1)], + witness, + ..Default::default() + }); + + let reinscription_on_cursed = InscriptionId { txid, index: 0 }; + + context.mine_blocks(1); + + context.index.assert_inscription_location( + reinscription_on_cursed, + SatPoint { + outpoint: unbound_outpoint(), + offset: 0, + }, + None, + ); + + assert_eq!( + context + .index + .get_inscription_entry(reinscription_on_cursed) + .unwrap() + .unwrap() + .number, + 1 + ); + + let witness = envelope(&[ + b"ord", + &[1], + b"text/plain;charset=utf-8", + &[], + b"second reinscription on cursed", + ]); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(4, 1, 0)], + witness, + ..Default::default() + }); + + let second_reinscription_on_cursed = InscriptionId { txid, index: 0 }; + + context.mine_blocks(1); + + context.index.assert_inscription_location( + second_reinscription_on_cursed, + SatPoint { + outpoint: unbound_outpoint(), + offset: 1, + }, + None, + ); + + assert_eq!( + context + .index + .get_inscription_entry(second_reinscription_on_cursed) + .unwrap() + .unwrap() + .number, + -2 + ); + } + } } From acf56b2e56806e1145f07615d74c0575823b0679 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 21 Jun 2023 15:18:49 +0200 Subject: [PATCH 02/14] second reinscription should be cursed --- src/index.rs | 102 ++++++++++++----------- src/index/updater.rs | 6 +- src/index/updater/inscription_updater.rs | 91 +++++++++++++++----- 3 files changed, 128 insertions(+), 71 deletions(-) diff --git a/src/index.rs b/src/index.rs index c65981955c..9eb3735070 100644 --- a/src/index.rs +++ b/src/index.rs @@ -13,7 +13,10 @@ use { chrono::SubsecRound, indicatif::{ProgressBar, ProgressStyle}, log::log_enabled, - redb::{Database, ReadableTable, Table, TableDefinition, WriteStrategy, WriteTransaction}, + redb::{ + Database, MultimapTable, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, Table, + TableDefinition, WriteStrategy, WriteTransaction, + }, std::collections::HashMap, std::sync::atomic::{self, AtomicBool}, }; @@ -23,7 +26,7 @@ mod fetcher; mod rtx; mod updater; -const SCHEMA_VERSION: u64 = 4; +const SCHEMA_VERSION: u64 = 5; macro_rules! define_table { ($name:ident, $key:ty, $value:ty) => { @@ -31,14 +34,22 @@ macro_rules! define_table { }; } +macro_rules! define_multimap_table { + ($name:ident, $key:ty, $value:ty) => { + const $name: MultimapTableDefinition<$key, $value> = + MultimapTableDefinition::new(stringify!($name)); + }; +} + 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, i64, &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! { REINSCRIPTION_ID_TO_SEQUENCE_NUMBER, &InscriptionIdValue, u64 } +define_multimap_table! { SATPOINT_TO_INSCRIPTION_ID, &SatPointValue, &InscriptionIdValue } +define_multimap_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 } @@ -197,8 +208,9 @@ impl Index { tx.open_table(INSCRIPTION_ID_TO_SATPOINT)?; tx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?; tx.open_table(OUTPOINT_TO_VALUE)?; - tx.open_table(SATPOINT_TO_INSCRIPTION_ID)?; - tx.open_table(SAT_TO_INSCRIPTION_ID)?; + tx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER)?; + tx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?; + tx.open_multimap_table(SAT_TO_INSCRIPTION_ID)?; tx.open_table(SAT_TO_SATPOINT)?; tx.open_table(WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP)?; @@ -494,8 +506,9 @@ impl Index { self .database .begin_read()? - .open_table(SAT_TO_INSCRIPTION_ID)? + .open_multimap_table(SAT_TO_INSCRIPTION_ID)? .get(&sat.n())? + .next() .map(|inscription_id| Entry::load(*inscription_id.value())), ) } @@ -558,7 +571,7 @@ impl Index { &self .database .begin_read()? - .open_table(SATPOINT_TO_INSCRIPTION_ID)?, + .open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?, outpoint, )? .map(|(_satpoint, inscription_id)| inscription_id) @@ -705,9 +718,11 @@ impl Index { self .database .begin_read()? - .open_table(SATPOINT_TO_INSCRIPTION_ID)? + .open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)? .range::<&[u8; 44]>(&[0; 44]..)? - .map(|(satpoint, id)| (Entry::load(*satpoint.value()), Entry::load(*id.value()))) + .flat_map(|(satpoint, id_iter)| { + id_iter.map(move |id| (Entry::load(*satpoint.value()), Entry::load(*id.value()))) + }) .take(n.unwrap_or(usize::MAX)) .collect(), ) @@ -810,7 +825,7 @@ impl Index { ) { let rtx = self.database.begin_read().unwrap(); - let satpoint_to_inscription_id = rtx.open_table(SATPOINT_TO_INSCRIPTION_ID).unwrap(); + let satpoint_to_inscription_id = rtx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID).unwrap(); let inscription_id_to_satpoint = rtx.open_table(INSCRIPTION_ID_TO_SATPOINT).unwrap(); @@ -830,34 +845,22 @@ impl Index { satpoint, ); - assert_eq!( - InscriptionId::load( - *satpoint_to_inscription_id - .get(&satpoint.store()) - .unwrap() - .unwrap() - .value() - ), - inscription_id, - ); + assert!(satpoint_to_inscription_id + .get(&satpoint.store()) + .unwrap() + .any(|id| InscriptionId::load(*id.value()) == inscription_id)); match sat { Some(sat) => { if self.has_sat_index().unwrap() { // unbound inscriptions should not be assigned to a sat assert!(satpoint.outpoint != unbound_outpoint()); - assert_eq!( - InscriptionId::load( - *rtx - .open_table(SAT_TO_INSCRIPTION_ID) - .unwrap() - .get(&sat) - .unwrap() - .unwrap() - .value() - ), - inscription_id, - ); + assert!(rtx + .open_multimap_table(SAT_TO_INSCRIPTION_ID) + .unwrap() + .get(&sat) + .unwrap() + .any(|id| InscriptionId::load(*id.value()) == inscription_id)); // we do not track common sats (only the sat ranges) if !Sat(sat).is_common() { @@ -885,7 +888,7 @@ impl Index { } fn inscriptions_on_output<'a: 'tx, 'tx>( - satpoint_to_id: &'a impl ReadableTable<&'static SatPointValue, &'static InscriptionIdValue>, + satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>, outpoint: OutPoint, ) -> Result + 'tx> { let start = SatPoint { @@ -903,7 +906,9 @@ impl Index { Ok( satpoint_to_id .range::<&[u8; 44]>(&start..=&end)? - .map(|(satpoint, id)| (Entry::load(*satpoint.value()), Entry::load(*id.value()))), + .flat_map(|(satpoint, id_iter)| { + id_iter.map(move |id| (Entry::load(*satpoint.value()), Entry::load(*id.value()))) + }), ) } } @@ -2137,7 +2142,7 @@ mod tests { } #[test] - fn inscriptions_on_same_sat_after_the_first_are_unbound() { + fn inscriptions_on_same_sat_after_the_first_are_not_unbound() { for context in Context::configurations() { context.mine_blocks(1); @@ -2187,10 +2192,13 @@ mod tests { context.index.assert_inscription_location( inscription_id, SatPoint { - outpoint: unbound_outpoint(), + outpoint: OutPoint { + txid: second, + vout: 0, + }, offset: 0, }, - None, // should not be on a sat + Some(50 * COIN_VALUE), ); assert!(context @@ -2701,7 +2709,7 @@ mod tests { } #[test] - fn reinscription_on_cursed_inscription_is_not_cursed_but_unbound() { + fn reinscription_on_cursed_inscription_is_not_cursed() { for context in Context::configurations() { context.mine_blocks(1); context.mine_blocks(1); @@ -2765,10 +2773,10 @@ mod tests { context.index.assert_inscription_location( reinscription_on_cursed, SatPoint { - outpoint: unbound_outpoint(), + outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, - None, + Some(100 * COIN_VALUE), ); assert_eq!( @@ -2784,7 +2792,7 @@ mod tests { } #[test] - fn second_reinscription_on_cursed_inscription_is_cursed_and_unbound() { + fn second_reinscription_on_cursed_inscription_is_cursed() { for context in Context::configurations() { context.mine_blocks(1); context.mine_blocks(1); @@ -2848,10 +2856,10 @@ mod tests { context.index.assert_inscription_location( reinscription_on_cursed, SatPoint { - outpoint: unbound_outpoint(), + outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, - None, + Some(100 * COIN_VALUE), ); assert_eq!( @@ -2885,10 +2893,10 @@ mod tests { context.index.assert_inscription_location( second_reinscription_on_cursed, SatPoint { - outpoint: unbound_outpoint(), - offset: 1, + outpoint: OutPoint { txid, vout: 0 }, + offset: 0, }, - None, + Some(100 * COIN_VALUE), ); assert_eq!( diff --git a/src/index/updater.rs b/src/index/updater.rs index 76e6a6ecc9..2def182443 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -408,8 +408,9 @@ impl Updater { let mut inscription_id_to_satpoint = wtx.open_table(INSCRIPTION_ID_TO_SATPOINT)?; let mut inscription_number_to_inscription_id = wtx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?; - let mut sat_to_inscription_id = wtx.open_table(SAT_TO_INSCRIPTION_ID)?; - let mut satpoint_to_inscription_id = wtx.open_table(SATPOINT_TO_INSCRIPTION_ID)?; + let mut reinscription_id_to_seq_num = wtx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER)?; + let mut sat_to_inscription_id = wtx.open_multimap_table(SAT_TO_INSCRIPTION_ID)?; + let mut satpoint_to_inscription_id = wtx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?; let mut statistic_to_count = wtx.open_table(STATISTIC_TO_COUNT)?; let mut lost_sats = statistic_to_count @@ -430,6 +431,7 @@ impl Updater { lost_sats, &mut inscription_number_to_inscription_id, &mut outpoint_to_value, + &mut reinscription_id_to_seq_num, &mut sat_to_inscription_id, &mut satpoint_to_inscription_id, block.header.time, diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 2b9abf7753..d1b516beec 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -31,8 +31,10 @@ pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { number_to_id: &'a mut Table<'db, 'tx, i64, &'static InscriptionIdValue>, outpoint_to_value: &'a mut Table<'db, 'tx, &'static OutPointValue, u64>, reward: u64, - sat_to_inscription_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, - satpoint_to_id: &'a mut Table<'db, 'tx, &'static SatPointValue, &'static InscriptionIdValue>, + reinscription_id_to_seq_num: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, u64>, + sat_to_inscription_id: &'a mut MultimapTable<'db, 'tx, u64, &'static InscriptionIdValue>, + satpoint_to_id: + &'a mut MultimapTable<'db, 'tx, &'static SatPointValue, &'static InscriptionIdValue>, timestamp: u32, pub(super) unbound_inscriptions: u64, value_cache: &'a mut HashMap, @@ -47,8 +49,14 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { lost_sats: u64, number_to_id: &'a mut Table<'db, 'tx, i64, &'static InscriptionIdValue>, outpoint_to_value: &'a mut Table<'db, 'tx, &'static OutPointValue, u64>, - sat_to_inscription_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, - satpoint_to_id: &'a mut Table<'db, 'tx, &'static SatPointValue, &'static InscriptionIdValue>, + reinscription_id_to_seq_num: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, u64>, + sat_to_inscription_id: &'a mut MultimapTable<'db, 'tx, u64, &'static InscriptionIdValue>, + satpoint_to_id: &'a mut MultimapTable< + 'db, + 'tx, + &'static SatPointValue, + &'static InscriptionIdValue, + >, timestamp: u32, unbound_inscriptions: u64, value_cache: &'a mut HashMap, @@ -78,6 +86,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { number_to_id, outpoint_to_value, reward: Height(height).subsidy(), + reinscription_id_to_seq_num, sat_to_inscription_id, satpoint_to_id, timestamp, @@ -107,7 +116,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { // find existing inscriptions on input aka transfers of inscriptions for (old_satpoint, inscription_id) in - Index::inscriptions_on_output(self.satpoint_to_id, tx_in.previous_output)? + self.inscriptions_on_output_ordered(self.satpoint_to_id, tx_in.previous_output)? { let offset = input_value + old_satpoint.offset; floating_inscriptions.push(Flotsam { @@ -116,7 +125,10 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { origin: Origin::Old { old_satpoint }, }); - inscribed_offsets.insert(offset, inscription_id); + inscribed_offsets + .entry(offset) + .and_modify(|(_id, count)| *count += 1) + .or_insert((inscription_id, 0)); } let offset = input_value; @@ -144,31 +156,40 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { break; } + let inscription_id = InscriptionId { + txid, + index: id_counter, + }; + + if inscribed_offsets.contains_key(&offset) { + let seq_num = self.reinscription_id_to_seq_num.len().unwrap_or(0) as u64 + 1; + self + .reinscription_id_to_seq_num + .insert(&inscription_id.store(), seq_num)?; + } + + let first_reinscription = inscribed_offsets + .get(&offset) + .and_then(|(_id, count)| Some(count == &0)) + .unwrap_or(false); + let initial_inscription_is_cursed = inscribed_offsets .get(&offset) - .and_then( - |inscription_id| match self.id_to_entry.get(&inscription_id.store()) { + .and_then(|(inscription_id, _count)| { + match self.id_to_entry.get(&inscription_id.store()) { Ok(option) => option.map(|entry| InscriptionEntry::load(entry.value()).number < 0), Err(_) => None, - }, - ) + } + }) .unwrap_or(false); - let cursed = !initial_inscription_is_cursed + let cursed = !(initial_inscription_is_cursed && first_reinscription) && (inscription.tx_in_index != 0 || inscription.tx_in_offset != 0 || inscribed_offsets.contains_key(&offset)); - // In this first part of the cursed inscriptions implementation we ignore reinscriptions. - // This will change once we implement reinscriptions. - let unbound = inscribed_offsets.contains_key(&offset) - || inscription.tx_in_offset != 0 - || input_value == 0; - - let inscription_id = InscriptionId { - txid, - index: id_counter, - }; + let unbound = inscription.tx_in_offset != 0 || input_value == 0; + // || inscribed_offsets.contains_key(&offset) floating_inscriptions.push(Flotsam { inscription_id, @@ -293,7 +314,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { let inscription_id = flotsam.inscription_id.store(); let unbound = match flotsam.origin { Origin::Old { old_satpoint } => { - self.satpoint_to_id.remove(&old_satpoint.store())?; + self.satpoint_to_id.remove_all(&old_satpoint.store())?; false } @@ -368,4 +389,30 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { Ok(()) } + + fn inscriptions_on_output_ordered( + &self, + satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>, + outpoint: OutPoint, + ) -> Result> { + let mut result = Index::inscriptions_on_output(satpoint_to_id, outpoint)? + .collect::>(); + + if result.len() <= 1 { + return Ok(result); + } + + result.sort_by_key(|(_satpoint, inscription_id)| { + match self + .reinscription_id_to_seq_num + .get(&inscription_id.store()) + { + Ok(Some(num)) => num.value(), + Ok(None) => 0, + _ => 0, // TODO + } + }); + + Ok(result) + } } From 98340591c2981bad6d3215b185e1c9bc7c3e0f66 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 21 Jun 2023 22:13:32 +0200 Subject: [PATCH 03/14] log everything --- src/index.rs | 97 ++++++++++++++++++++++++ src/index/updater/inscription_updater.rs | 36 ++++++++- 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/src/index.rs b/src/index.rs index 9eb3735070..e9dbc2b31d 100644 --- a/src/index.rs +++ b/src/index.rs @@ -579,6 +579,23 @@ impl Index { ) } + pub(crate) fn get_inscriptions_on_output_ordered( + &self, + outpoint: OutPoint, + ) -> Result> { + let rtx = &self.database.begin_read()?; + + let sat_to_id = rtx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?; + + let re_id_to_seq_num = rtx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER)?; + + Ok(Self::inscriptions_on_output_ordered( + &re_id_to_seq_num, + &sat_to_id, + outpoint, + )?) + } + pub(crate) fn get_transaction(&self, txid: Txid) -> Result> { if txid == self.genesis_block_coinbase_txid { Ok(Some(self.genesis_block_coinbase_transaction.clone())) @@ -911,6 +928,30 @@ impl Index { }), ) } + + fn inscriptions_on_output_ordered<'a: 'tx, 'tx>( + re_id_to_seq_num: &'a impl ReadableTable<&'static InscriptionIdValue, u64>, + satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>, + outpoint: OutPoint, + ) -> Result> { + let mut result = Self::inscriptions_on_output(satpoint_to_id, outpoint)? + .map(|(_satpoint, inscription_id)| inscription_id) + .collect::>(); + + if result.len() <= 1 { + return Ok(result); + } + + result.sort_by_key(|inscription_id| { + match re_id_to_seq_num.get(&inscription_id.store()) { + Ok(Some(num)) => num.value(), + Ok(None) => 0, + _ => 0, // TODO + } + }); + + Ok(result) + } } #[cfg(test)] @@ -2908,6 +2949,62 @@ mod tests { .number, -2 ); + + assert_eq!( + vec![ + cursed, + reinscription_on_cursed, + second_reinscription_on_cursed + ], + context + .index + .get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 }) + .unwrap() + ) + } + } + + #[test] + fn reinscriptions_on_output_correctly_ordered() { + for context in Context::configurations() { + context.mine_blocks(1); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0)], + witness: inscription("text/plain;charset=utf-8", "hello").to_witness(), + ..Default::default() + }); + + let first = InscriptionId { txid, index: 0 }; + + context.mine_blocks(1); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(2, 1, 0)], + witness: inscription("text/plain;charset=utf-8", "hello").to_witness(), + ..Default::default() + }); + + let second = InscriptionId { txid, index: 0 }; + + context.mine_blocks(1); + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(3, 1, 0)], + witness: inscription("text/plain;charset=utf-8", "hello").to_witness(), + ..Default::default() + }); + + let third = InscriptionId { txid, index: 0 }; + + context.mine_blocks(1); + + assert_eq!( + vec![first, second, third], + context + .index + .get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 }) + .unwrap() + ) } } } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index d1b516beec..048ea31a4f 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -129,6 +129,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .entry(offset) .and_modify(|(_id, count)| *count += 1) .or_insert((inscription_id, 0)); + + log::info!("{:?}", inscribed_offsets); } let offset = input_value; @@ -161,11 +163,39 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { index: id_counter, }; + // if reinscription track it's ordering if inscribed_offsets.contains_key(&offset) { - let seq_num = self.reinscription_id_to_seq_num.len().unwrap_or(0) as u64 + 1; + let seq_num = self + .reinscription_id_to_seq_num + .iter()? + .rev() + .next() + .map(|(_id, number)| number.value() + 1) + .unwrap_or(0); + + log::info!("ID: {inscription_id}\nsequence number: {seq_num}\n"); self .reinscription_id_to_seq_num .insert(&inscription_id.store(), seq_num)?; + + if let Some(sat_ranges) = input_sat_ranges { + let mut previous_ranges_size = 0; + for range in sat_ranges { + if offset <= previous_ranges_size + (range.1 - range.0) { + log::info!( + "reinscription {} on sat {} at offset {} in range ({}, {}) ({:?})", + inscription_id, + range.0 + offset, + offset, + range.0, + range.1, + sat_ranges + ); + break; + } + previous_ranges_size += range.1 - range.0; + } + } } let first_reinscription = inscribed_offsets @@ -173,6 +203,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .and_then(|(_id, count)| Some(count == &0)) .unwrap_or(false); + log::info!("is first reinscription: {first_reinscription}"); + let initial_inscription_is_cursed = inscribed_offsets .get(&offset) .and_then(|(inscription_id, _count)| { @@ -183,6 +215,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { }) .unwrap_or(false); + log::info!("initial inscription cursed: {initial_inscription_is_cursed}"); + let cursed = !(initial_inscription_is_cursed && first_reinscription) && (inscription.tx_in_index != 0 || inscription.tx_in_offset != 0 From e32a17ff56623cbb5df60412b3ac11246d98f2ca Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 21 Jun 2023 23:05:34 +0200 Subject: [PATCH 04/14] make more efficient --- src/index/updater/inscription_updater.rs | 47 +++++++++++++----------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 048ea31a4f..6540502333 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -129,8 +129,6 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .entry(offset) .and_modify(|(_id, count)| *count += 1) .or_insert((inscription_id, 0)); - - log::info!("{:?}", inscribed_offsets); } let offset = input_value; @@ -165,6 +163,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { // if reinscription track it's ordering if inscribed_offsets.contains_key(&offset) { + log::info!("{:?}", inscribed_offsets); + let seq_num = self .reinscription_id_to_seq_num .iter()? @@ -174,6 +174,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .unwrap_or(0); log::info!("ID: {inscription_id}\nsequence number: {seq_num}\n"); + self .reinscription_id_to_seq_num .insert(&inscription_id.store(), seq_num)?; @@ -198,29 +199,33 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } - let first_reinscription = inscribed_offsets - .get(&offset) - .and_then(|(_id, count)| Some(count == &0)) - .unwrap_or(false); + let cursed = inscription.tx_in_index != 0 + || inscription.tx_in_offset != 0 + || inscribed_offsets.contains_key(&offset); - log::info!("is first reinscription: {first_reinscription}"); + let cursed = if cursed { + let first_reinscription = inscribed_offsets + .get(&offset) + .and_then(|(_id, count)| Some(count == &0)) + .unwrap_or(false); - let initial_inscription_is_cursed = inscribed_offsets - .get(&offset) - .and_then(|(inscription_id, _count)| { - match self.id_to_entry.get(&inscription_id.store()) { - Ok(option) => option.map(|entry| InscriptionEntry::load(entry.value()).number < 0), - Err(_) => None, - } - }) - .unwrap_or(false); + log::info!("is first reinscription: {first_reinscription}"); - log::info!("initial inscription cursed: {initial_inscription_is_cursed}"); + let initial_inscription_is_cursed = inscribed_offsets + .get(&offset) + .and_then(|(inscription_id, _count)| { + match self.id_to_entry.get(&inscription_id.store()) { + Ok(option) => option.map(|entry| InscriptionEntry::load(entry.value()).number < 0), + Err(_) => None, + } + }) + .unwrap_or(false); - let cursed = !(initial_inscription_is_cursed && first_reinscription) - && (inscription.tx_in_index != 0 - || inscription.tx_in_offset != 0 - || inscribed_offsets.contains_key(&offset)); + log::info!("initial inscription cursed: {initial_inscription_is_cursed}"); + !(initial_inscription_is_cursed && first_reinscription) + } else { + cursed + }; let unbound = inscription.tx_in_offset != 0 || input_value == 0; // || inscribed_offsets.contains_key(&offset) From 921fb99ec9c3203f14137a8cd3099976dfcf2c88 Mon Sep 17 00:00:00 2001 From: ordinally Date: Thu, 22 Jun 2023 11:57:39 +0200 Subject: [PATCH 05/14] Precursed era --- src/index/updater/inscription_updater.rs | 64 +++++++++++++----------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 6540502333..b84de33d19 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -41,6 +41,9 @@ pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { } impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { + const CURSED_ERA_START_HEIGHT: u64 = 792_876; // deployment block height for ord 0.6.0 introducing cursed inscriptions on ordinals.com - everything before that is precursed. + // TODO: this is mainnet only, need to make this configurable + pub(super) fn new( height: u64, id_to_satpoint: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static SatPointValue>, @@ -161,10 +164,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { index: id_counter, }; - // if reinscription track it's ordering + // if reinscription track its ordering if inscribed_offsets.contains_key(&offset) { - log::info!("{:?}", inscribed_offsets); - let seq_num = self .reinscription_id_to_seq_num .iter()? @@ -173,62 +174,45 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .map(|(_id, number)| number.value() + 1) .unwrap_or(0); - log::info!("ID: {inscription_id}\nsequence number: {seq_num}\n"); + let sat = Self::calculate_sat(input_sat_ranges, offset); + log::info!("processing reinscription {inscription_id} on sat {:?}: sequence number {seq_num}, inscribed offsets {:?}", sat, inscribed_offsets); self .reinscription_id_to_seq_num .insert(&inscription_id.store(), seq_num)?; - - if let Some(sat_ranges) = input_sat_ranges { - let mut previous_ranges_size = 0; - for range in sat_ranges { - if offset <= previous_ranges_size + (range.1 - range.0) { - log::info!( - "reinscription {} on sat {} at offset {} in range ({}, {}) ({:?})", - inscription_id, - range.0 + offset, - offset, - range.0, - range.1, - sat_ranges - ); - break; - } - previous_ranges_size += range.1 - range.0; - } - } } let cursed = inscription.tx_in_index != 0 || inscription.tx_in_offset != 0 || inscribed_offsets.contains_key(&offset); + // special case to keep inscription numbers stable for reinscribed inscriptions where an initial cursed inscription was made in the precursed era, that inscription was then reinscribed (but not recognized as a reinscription by ord prior to 0.6.0), and thus got assigned a normal positive inscription number. let cursed = if cursed { let first_reinscription = inscribed_offsets .get(&offset) .and_then(|(_id, count)| Some(count == &0)) .unwrap_or(false); - log::info!("is first reinscription: {first_reinscription}"); - - let initial_inscription_is_cursed = inscribed_offsets + let initial_inscription_is_precursed = inscribed_offsets .get(&offset) .and_then(|(inscription_id, _count)| { match self.id_to_entry.get(&inscription_id.store()) { - Ok(option) => option.map(|entry| InscriptionEntry::load(entry.value()).number < 0), + Ok(option) => option.map(|entry| { + let loaded_entry = InscriptionEntry::load(entry.value()); + loaded_entry.number < 0 || loaded_entry.height < Self::CURSED_ERA_START_HEIGHT + }), Err(_) => None, } }) .unwrap_or(false); + log::info!("{inscription_id}: is first reinscription: {first_reinscription}, initial inscription is precursed: {initial_inscription_is_precursed}"); - log::info!("initial inscription cursed: {initial_inscription_is_cursed}"); - !(initial_inscription_is_cursed && first_reinscription) + !(initial_inscription_is_precursed && first_reinscription) } else { cursed }; let unbound = inscription.tx_in_offset != 0 || input_value == 0; - // || inscribed_offsets.contains_key(&offset) floating_inscriptions.push(Flotsam { inscription_id, @@ -344,6 +328,26 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } + fn calculate_sat( + input_sat_ranges: Option<&VecDeque<(u64, u64)>>, + input_offset: u64, + ) -> Option { + let mut sat = None; + if let Some(input_sat_ranges) = input_sat_ranges { + let mut offset = 0; + for (start, end) in input_sat_ranges { + let size = end - start; + if offset + size > input_offset { + let n = start + input_offset - offset; + sat = Some(Sat(n)); + break; + } + offset += size; + } + } + sat + } + fn update_inscription_location( &mut self, input_sat_ranges: Option<&VecDeque<(u64, u64)>>, From 5ce1469f1205e3aa736903d9d5294ec8c4ed3b59 Mon Sep 17 00:00:00 2001 From: ordinally Date: Thu, 22 Jun 2023 15:01:47 +0200 Subject: [PATCH 06/14] Duh --- src/index/updater/inscription_updater.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index b84de33d19..104de82903 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -199,7 +199,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { match self.id_to_entry.get(&inscription_id.store()) { Ok(option) => option.map(|entry| { let loaded_entry = InscriptionEntry::load(entry.value()); - loaded_entry.number < 0 || loaded_entry.height < Self::CURSED_ERA_START_HEIGHT + loaded_entry.number < 0 && loaded_entry.height < Self::CURSED_ERA_START_HEIGHT }), Err(_) => None, } From daa0c6d40dc9e17bf9752e1ea64ee22bb69a57e4 Mon Sep 17 00:00:00 2001 From: ordinally Date: Thu, 22 Jun 2023 16:44:29 +0200 Subject: [PATCH 07/14] Make era height chain dependent --- src/chain.rs | 11 +++++++++++ src/index.rs | 4 ++++ src/index/updater.rs | 3 +++ src/index/updater/inscription_updater.rs | 9 +++++---- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/chain.rs b/src/chain.rs index 8145578ea6..c05c202c7c 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -31,6 +31,17 @@ impl Chain { } } + pub(crate) fn cursed_era_start_height(self) -> u64 { + // deployment block height for ord 0.6.0 introducing cursed inscriptions on ordinals.com - everything before that is precursed. + // TODO: this is mainnet only, need to make this configurable + match self { + Self::Mainnet => 792_876, + Self::Regtest => 11, + Self::Signet => 38332, // FIXME + Self::Testnet => 18332, + } + } + pub(crate) fn inscription_content_size_limit(self) -> Option { match self { Self::Mainnet | Self::Regtest => None, diff --git a/src/index.rs b/src/index.rs index e9dbc2b31d..e1f6185960 100644 --- a/src/index.rs +++ b/src/index.rs @@ -245,6 +245,10 @@ impl Index { }) } + pub(crate) fn chain(&self) -> Chain { + self.options.chain() + } + pub(crate) fn get_unspent_outputs(&self, _wallet: Wallet) -> Result> { let mut utxos = BTreeMap::new(); utxos.extend( diff --git a/src/index/updater.rs b/src/index/updater.rs index 2def182443..9885333255 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -37,6 +37,7 @@ pub(crate) struct Updater { outputs_cached: u64, outputs_inserted_since_flush: u64, outputs_traversed: u64, + chain: Chain, } impl Updater { @@ -69,6 +70,7 @@ impl Updater { outputs_cached: 0, outputs_inserted_since_flush: 0, outputs_traversed: 0, + chain: index.chain(), }; updater.update_index(index, wtx) @@ -437,6 +439,7 @@ impl Updater { block.header.time, unbound_inscriptions, value_cache, + self.chain, )?; if self.index_sats { diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 104de82903..d320b6a21b 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -38,12 +38,10 @@ pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { timestamp: u32, pub(super) unbound_inscriptions: u64, value_cache: &'a mut HashMap, + chain: Chain, } impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { - const CURSED_ERA_START_HEIGHT: u64 = 792_876; // deployment block height for ord 0.6.0 introducing cursed inscriptions on ordinals.com - everything before that is precursed. - // TODO: this is mainnet only, need to make this configurable - pub(super) fn new( height: u64, id_to_satpoint: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static SatPointValue>, @@ -63,6 +61,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { timestamp: u32, unbound_inscriptions: u64, value_cache: &'a mut HashMap, + chain: Chain, ) -> Result { let next_cursed_number = number_to_id .iter()? @@ -95,6 +94,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { timestamp, unbound_inscriptions, value_cache, + chain, }) } @@ -199,7 +199,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { match self.id_to_entry.get(&inscription_id.store()) { Ok(option) => option.map(|entry| { let loaded_entry = InscriptionEntry::load(entry.value()); - loaded_entry.number < 0 && loaded_entry.height < Self::CURSED_ERA_START_HEIGHT + loaded_entry.number < 0 + && loaded_entry.height < self.chain.cursed_era_start_height() }), Err(_) => None, } From 466a1ba19fabdf7a215c88e8dc862b94c5e0cfc2 Mon Sep 17 00:00:00 2001 From: ordinally Date: Fri, 23 Jun 2023 11:45:46 +0200 Subject: [PATCH 08/14] Add CursedType enum Improve logging --- src/index/updater/inscription_updater.rs | 33 +++++++++++++++++++----- src/inscription.rs | 8 ++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index d320b6a21b..6b4f959e89 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -164,8 +164,22 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { index: id_counter, }; + let mut curse = inscription::CursedType::NotCursed; + + if inscription.tx_in_index != 0 { + curse = inscription::CursedType::NotFirstInput; + } else if inscription.tx_in_offset != 0 { + curse = inscription::CursedType::NotOffsetZero; + } else if inscribed_offsets.contains_key(&offset) { + curse = inscription::CursedType::Reinscription; + } + + if curse != inscription::CursedType::NotCursed { + log::info!("found cursed inscription {inscription_id}: {:?}", curse); + } + // if reinscription track its ordering - if inscribed_offsets.contains_key(&offset) { + if curse == inscription::CursedType::Reinscription { let seq_num = self .reinscription_id_to_seq_num .iter()? @@ -182,12 +196,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .insert(&inscription_id.store(), seq_num)?; } - let cursed = inscription.tx_in_index != 0 - || inscription.tx_in_offset != 0 - || inscribed_offsets.contains_key(&offset); - // special case to keep inscription numbers stable for reinscribed inscriptions where an initial cursed inscription was made in the precursed era, that inscription was then reinscribed (but not recognized as a reinscription by ord prior to 0.6.0), and thus got assigned a normal positive inscription number. - let cursed = if cursed { + let cursed = if curse == inscription::CursedType::Reinscription { let first_reinscription = inscribed_offsets .get(&offset) .and_then(|(_id, count)| Some(count == &0)) @@ -210,11 +220,20 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { !(initial_inscription_is_precursed && first_reinscription) } else { - cursed + curse != inscription::CursedType::NotCursed }; let unbound = inscription.tx_in_offset != 0 || input_value == 0; + if curse != inscription::CursedType::NotCursed || unbound { + log::info!( + "indexing inscription {inscription_id} with curse {:?} as cursed {} and unbound {}", + curse, + cursed, + unbound + ); + } + floating_inscriptions.push(Flotsam { inscription_id, offset, diff --git a/src/inscription.rs b/src/inscription.rs index abdbdd0483..ae83edc44d 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -20,6 +20,14 @@ const PROTOCOL_ID: &[u8] = b"ord"; const BODY_TAG: &[u8] = &[]; const CONTENT_TYPE_TAG: &[u8] = &[1]; +#[derive(Debug, PartialEq, Clone)] +pub(crate) enum CursedType { + NotCursed, + NotFirstInput, + NotOffsetZero, + Reinscription, +} + #[derive(Debug, PartialEq, Clone)] pub(crate) struct Inscription { body: Option>, From 9ddfe32a337407597831da0e0b4cd3e90dfb1ed3 Mon Sep 17 00:00:00 2001 From: ordinally Date: Fri, 23 Jun 2023 14:28:52 +0200 Subject: [PATCH 09/14] - precursed area concept not required --- src/chain.rs | 11 ----------- src/index.rs | 4 ---- src/index/updater.rs | 3 --- src/index/updater/inscription_updater.rs | 10 +++------- 4 files changed, 3 insertions(+), 25 deletions(-) diff --git a/src/chain.rs b/src/chain.rs index c05c202c7c..8145578ea6 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -31,17 +31,6 @@ impl Chain { } } - pub(crate) fn cursed_era_start_height(self) -> u64 { - // deployment block height for ord 0.6.0 introducing cursed inscriptions on ordinals.com - everything before that is precursed. - // TODO: this is mainnet only, need to make this configurable - match self { - Self::Mainnet => 792_876, - Self::Regtest => 11, - Self::Signet => 38332, // FIXME - Self::Testnet => 18332, - } - } - pub(crate) fn inscription_content_size_limit(self) -> Option { match self { Self::Mainnet | Self::Regtest => None, diff --git a/src/index.rs b/src/index.rs index e1f6185960..e9dbc2b31d 100644 --- a/src/index.rs +++ b/src/index.rs @@ -245,10 +245,6 @@ impl Index { }) } - pub(crate) fn chain(&self) -> Chain { - self.options.chain() - } - pub(crate) fn get_unspent_outputs(&self, _wallet: Wallet) -> Result> { let mut utxos = BTreeMap::new(); utxos.extend( diff --git a/src/index/updater.rs b/src/index/updater.rs index 9885333255..2def182443 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -37,7 +37,6 @@ pub(crate) struct Updater { outputs_cached: u64, outputs_inserted_since_flush: u64, outputs_traversed: u64, - chain: Chain, } impl Updater { @@ -70,7 +69,6 @@ impl Updater { outputs_cached: 0, outputs_inserted_since_flush: 0, outputs_traversed: 0, - chain: index.chain(), }; updater.update_index(index, wtx) @@ -439,7 +437,6 @@ impl Updater { block.header.time, unbound_inscriptions, value_cache, - self.chain, )?; if self.index_sats { diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 6b4f959e89..387d35dbe8 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -38,7 +38,6 @@ pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { timestamp: u32, pub(super) unbound_inscriptions: u64, value_cache: &'a mut HashMap, - chain: Chain, } impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { @@ -61,7 +60,6 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { timestamp: u32, unbound_inscriptions: u64, value_cache: &'a mut HashMap, - chain: Chain, ) -> Result { let next_cursed_number = number_to_id .iter()? @@ -94,7 +92,6 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { timestamp, unbound_inscriptions, value_cache, - chain, }) } @@ -203,22 +200,21 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .and_then(|(_id, count)| Some(count == &0)) .unwrap_or(false); - let initial_inscription_is_precursed = inscribed_offsets + let initial_inscription_is_cursed = inscribed_offsets .get(&offset) .and_then(|(inscription_id, _count)| { match self.id_to_entry.get(&inscription_id.store()) { Ok(option) => option.map(|entry| { let loaded_entry = InscriptionEntry::load(entry.value()); loaded_entry.number < 0 - && loaded_entry.height < self.chain.cursed_era_start_height() }), Err(_) => None, } }) .unwrap_or(false); - log::info!("{inscription_id}: is first reinscription: {first_reinscription}, initial inscription is precursed: {initial_inscription_is_precursed}"); + log::info!("{inscription_id}: is first reinscription: {first_reinscription}, initial inscription is cursed: {initial_inscription_is_cursed}"); - !(initial_inscription_is_precursed && first_reinscription) + !(initial_inscription_is_cursed && first_reinscription) } else { curse != inscription::CursedType::NotCursed }; From 1b1a79c105d2e8818f6e07dbf9084102fcd7242b Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 23 Jun 2023 18:44:25 +0200 Subject: [PATCH 10/14] rename cursed type and remove comment --- src/index/updater/inscription_updater.rs | 21 ++++++++++----------- src/inscription.rs | 7 +++---- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 387d35dbe8..2ae461a9c3 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -1,4 +1,4 @@ -use super::*; +use {super::*, inscription::Curse}; #[derive(Debug, Clone)] pub(super) struct Flotsam { @@ -161,22 +161,22 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { index: id_counter, }; - let mut curse = inscription::CursedType::NotCursed; + let mut curse = None; if inscription.tx_in_index != 0 { - curse = inscription::CursedType::NotFirstInput; + curse = Some(Curse::NotInFirstInput); } else if inscription.tx_in_offset != 0 { - curse = inscription::CursedType::NotOffsetZero; + curse = Some(Curse::NotAtOffsetZero); } else if inscribed_offsets.contains_key(&offset) { - curse = inscription::CursedType::Reinscription; + curse = Some(Curse::Reinscription); } - if curse != inscription::CursedType::NotCursed { + if curse != None { log::info!("found cursed inscription {inscription_id}: {:?}", curse); } // if reinscription track its ordering - if curse == inscription::CursedType::Reinscription { + if curse == Some(Curse::Reinscription) { let seq_num = self .reinscription_id_to_seq_num .iter()? @@ -193,8 +193,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .insert(&inscription_id.store(), seq_num)?; } - // special case to keep inscription numbers stable for reinscribed inscriptions where an initial cursed inscription was made in the precursed era, that inscription was then reinscribed (but not recognized as a reinscription by ord prior to 0.6.0), and thus got assigned a normal positive inscription number. - let cursed = if curse == inscription::CursedType::Reinscription { + let cursed = if curse == Some(Curse::Reinscription) { let first_reinscription = inscribed_offsets .get(&offset) .and_then(|(_id, count)| Some(count == &0)) @@ -216,12 +215,12 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { !(initial_inscription_is_cursed && first_reinscription) } else { - curse != inscription::CursedType::NotCursed + curse != None }; let unbound = inscription.tx_in_offset != 0 || input_value == 0; - if curse != inscription::CursedType::NotCursed || unbound { + if curse != None || unbound { log::info!( "indexing inscription {inscription_id} with curse {:?} as cursed {} and unbound {}", curse, diff --git a/src/inscription.rs b/src/inscription.rs index ae83edc44d..60365a5245 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -21,10 +21,9 @@ const BODY_TAG: &[u8] = &[]; const CONTENT_TYPE_TAG: &[u8] = &[1]; #[derive(Debug, PartialEq, Clone)] -pub(crate) enum CursedType { - NotCursed, - NotFirstInput, - NotOffsetZero, +pub(crate) enum Curse { + NotInFirstInput, + NotAtOffsetZero, Reinscription, } From bbd2210f198e66868aa35fea2a124a95df11a068 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 23 Jun 2023 18:47:22 +0200 Subject: [PATCH 11/14] consolidate curse logic --- src/index/updater/inscription_updater.rs | 26 +++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 2ae461a9c3..64975e4433 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -161,22 +161,11 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { index: id_counter, }; - let mut curse = None; - - if inscription.tx_in_index != 0 { - curse = Some(Curse::NotInFirstInput); + let curse = if inscription.tx_in_index != 0 { + Some(Curse::NotInFirstInput) } else if inscription.tx_in_offset != 0 { - curse = Some(Curse::NotAtOffsetZero); + Some(Curse::NotAtOffsetZero) } else if inscribed_offsets.contains_key(&offset) { - curse = Some(Curse::Reinscription); - } - - if curse != None { - log::info!("found cursed inscription {inscription_id}: {:?}", curse); - } - - // if reinscription track its ordering - if curse == Some(Curse::Reinscription) { let seq_num = self .reinscription_id_to_seq_num .iter()? @@ -188,9 +177,18 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { let sat = Self::calculate_sat(input_sat_ranges, offset); log::info!("processing reinscription {inscription_id} on sat {:?}: sequence number {seq_num}, inscribed offsets {:?}", sat, inscribed_offsets); + // if reinscription track its ordering self .reinscription_id_to_seq_num .insert(&inscription_id.store(), seq_num)?; + + Some(Curse::Reinscription) + } else { + None + }; + + if curse != None { + log::info!("found cursed inscription {inscription_id}: {:?}", curse); } let cursed = if curse == Some(Curse::Reinscription) { From 626d39bf2aaab2d15fcc1c551a066a9e88362b88 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 23 Jun 2023 19:02:25 +0200 Subject: [PATCH 12/14] cosmetic things --- src/index/updater/inscription_updater.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 64975e4433..66b357c5fa 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -191,7 +191,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { log::info!("found cursed inscription {inscription_id}: {:?}", curse); } - let cursed = if curse == Some(Curse::Reinscription) { + let cursed = if let Some(Curse::Reinscription) = curse { let first_reinscription = inscribed_offsets .get(&offset) .and_then(|(_id, count)| Some(count == &0)) @@ -209,6 +209,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } }) .unwrap_or(false); + log::info!("{inscription_id}: is first reinscription: {first_reinscription}, initial inscription is cursed: {initial_inscription_is_cursed}"); !(initial_inscription_is_cursed && first_reinscription) @@ -216,7 +217,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { curse != None }; - let unbound = inscription.tx_in_offset != 0 || input_value == 0; + let unbound = input_value == 0 || inscription.tx_in_offset != 0; if curse != None || unbound { log::info!( From 96b9c76587daef22919ef404c4e5a428da545b7c Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 23 Jun 2023 19:19:30 +0200 Subject: [PATCH 13/14] clippy + fmt --- src/index.rs | 24 +++++++------- src/index/updater/inscription_updater.rs | 42 +++++------------------- 2 files changed, 22 insertions(+), 44 deletions(-) diff --git a/src/index.rs b/src/index.rs index e9dbc2b31d..4ea513da6b 100644 --- a/src/index.rs +++ b/src/index.rs @@ -579,21 +579,18 @@ impl Index { ) } + #[cfg(test)] pub(crate) fn get_inscriptions_on_output_ordered( &self, outpoint: OutPoint, - ) -> Result> { + ) -> Result> { let rtx = &self.database.begin_read()?; let sat_to_id = rtx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?; let re_id_to_seq_num = rtx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER)?; - Ok(Self::inscriptions_on_output_ordered( - &re_id_to_seq_num, - &sat_to_id, - outpoint, - )?) + Self::inscriptions_on_output_ordered(&re_id_to_seq_num, &sat_to_id, outpoint) } pub(crate) fn get_transaction(&self, txid: Txid) -> Result> { @@ -933,20 +930,19 @@ impl Index { re_id_to_seq_num: &'a impl ReadableTable<&'static InscriptionIdValue, u64>, satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>, outpoint: OutPoint, - ) -> Result> { + ) -> Result> { let mut result = Self::inscriptions_on_output(satpoint_to_id, outpoint)? - .map(|(_satpoint, inscription_id)| inscription_id) - .collect::>(); + .collect::>(); if result.len() <= 1 { return Ok(result); } - result.sort_by_key(|inscription_id| { + result.sort_by_key(|(_satpoint, inscription_id)| { match re_id_to_seq_num.get(&inscription_id.store()) { Ok(Some(num)) => num.value(), Ok(None) => 0, - _ => 0, // TODO + _ => 0, } }); @@ -2960,6 +2956,9 @@ mod tests { .index .get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 }) .unwrap() + .iter() + .map(|(_satpoint, inscription_id)| *inscription_id) + .collect::>() ) } } @@ -3004,6 +3003,9 @@ mod tests { .index .get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 }) .unwrap() + .iter() + .map(|(_satpoint, inscription_id)| *inscription_id) + .collect::>() ) } } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 66b357c5fa..b6b5b8dc95 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -115,9 +115,11 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } // find existing inscriptions on input aka transfers of inscriptions - for (old_satpoint, inscription_id) in - self.inscriptions_on_output_ordered(self.satpoint_to_id, tx_in.previous_output)? - { + for (old_satpoint, inscription_id) in Index::inscriptions_on_output_ordered( + self.reinscription_id_to_seq_num, + self.satpoint_to_id, + tx_in.previous_output, + )? { let offset = input_value + old_satpoint.offset; floating_inscriptions.push(Flotsam { offset, @@ -187,14 +189,14 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { None }; - if curse != None { + if curse.is_some() { log::info!("found cursed inscription {inscription_id}: {:?}", curse); } let cursed = if let Some(Curse::Reinscription) = curse { let first_reinscription = inscribed_offsets .get(&offset) - .and_then(|(_id, count)| Some(count == &0)) + .map(|(_id, count)| count == &0) .unwrap_or(false); let initial_inscription_is_cursed = inscribed_offsets @@ -214,12 +216,12 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { !(initial_inscription_is_cursed && first_reinscription) } else { - curse != None + curse.is_some() }; let unbound = input_value == 0 || inscription.tx_in_offset != 0; - if curse != None || unbound { + if curse.is_some() || unbound { log::info!( "indexing inscription {inscription_id} with curse {:?} as cursed {} and unbound {}", curse, @@ -446,30 +448,4 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { Ok(()) } - - fn inscriptions_on_output_ordered( - &self, - satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>, - outpoint: OutPoint, - ) -> Result> { - let mut result = Index::inscriptions_on_output(satpoint_to_id, outpoint)? - .collect::>(); - - if result.len() <= 1 { - return Ok(result); - } - - result.sort_by_key(|(_satpoint, inscription_id)| { - match self - .reinscription_id_to_seq_num - .get(&inscription_id.store()) - { - Ok(Some(num)) => num.value(), - Ok(None) => 0, - _ => 0, // TODO - } - }); - - Ok(result) - } } From a3b8690d13469ec89f89c850d69920f3a3275c28 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 23 Jun 2023 20:08:38 +0200 Subject: [PATCH 14/14] tweak test --- src/index.rs | 12 +++++++----- src/index/updater/inscription_updater.rs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/index.rs b/src/index.rs index 4ea513da6b..a1c1c8fcd2 100644 --- a/src/index.rs +++ b/src/index.rs @@ -2964,7 +2964,7 @@ mod tests { } #[test] - fn reinscriptions_on_output_correctly_ordered() { + fn reinscriptions_on_output_correctly_ordered_and_transferred() { for context in Context::configurations() { context.mine_blocks(1); @@ -2997,15 +2997,17 @@ mod tests { context.mine_blocks(1); + let location = SatPoint { + outpoint: OutPoint { txid, vout: 0 }, + offset: 0, + }; + assert_eq!( - vec![first, second, third], + vec![(location, first), (location, second), (location, third)], context .index .get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 }) .unwrap() - .iter() - .map(|(_satpoint, inscription_id)| *inscription_id) - .collect::>() ) } } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index b6b5b8dc95..1305c3f62a 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -114,7 +114,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { continue; } - // find existing inscriptions on input aka transfers of inscriptions + // find existing inscriptions on input (transfers of inscriptions) for (old_satpoint, inscription_id) in Index::inscriptions_on_output_ordered( self.reinscription_id_to_seq_num, self.satpoint_to_id,