diff --git a/src/index.rs b/src/index.rs index 520b3c6452..346836c82f 100644 --- a/src/index.rs +++ b/src/index.rs @@ -29,7 +29,7 @@ mod reorg; mod rtx; mod updater; -const SCHEMA_VERSION: u64 = 6; +const SCHEMA_VERSION: u64 = 7; macro_rules! define_table { ($name:ident, $key:ty, $value:ty) => { @@ -48,14 +48,13 @@ define_multimap_table! { INSCRIPTION_ID_TO_CHILDREN, &InscriptionIdValue, &Inscr define_multimap_table! { SATPOINT_TO_INSCRIPTION_ID, &SatPointValue, &InscriptionIdValue } define_multimap_table! { SAT_TO_INSCRIPTION_ID, u64, &InscriptionIdValue } define_table! { HEIGHT_TO_BLOCK_HASH, u64, &BlockHashValue } -define_table! { HEIGHT_TO_LAST_INSCRIPTION_NUMBER, u64, (i64, i64) } +define_table! { HEIGHT_TO_LAST_SEQUENCE_NUMBER, u64, u64 } 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! { REINSCRIPTION_ID_TO_SEQUENCE_NUMBER, &InscriptionIdValue, u64 } define_table! { SAT_TO_SATPOINT, u64, &SatPointValue } +define_table! { SEQUENCE_NUMBER_TO_INSCRIPTION_ID, u64, &InscriptionIdValue } define_table! { STATISTIC_TO_COUNT, u64, u64 } define_table! { WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP, u64, u128 } @@ -74,6 +73,8 @@ pub(crate) enum Statistic { OutputsTraversed = 3, SatRanges = 4, UnboundInscriptions = 5, + CursedInscriptions = 6, + BlessedInscriptions = 7, } impl Statistic { @@ -238,13 +239,12 @@ impl Index { tx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?; tx.open_multimap_table(SAT_TO_INSCRIPTION_ID)?; tx.open_table(HEIGHT_TO_BLOCK_HASH)?; - tx.open_table(HEIGHT_TO_LAST_INSCRIPTION_NUMBER)?; + tx.open_table(HEIGHT_TO_LAST_SEQUENCE_NUMBER)?; tx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; tx.open_table(INSCRIPTION_ID_TO_SATPOINT)?; - tx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?; tx.open_table(OUTPOINT_TO_VALUE)?; - tx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER)?; tx.open_table(SAT_TO_SATPOINT)?; + tx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)?; tx.open_table(WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP)?; tx.open_table(STATISTIC_TO_COUNT)? @@ -454,11 +454,10 @@ impl Index { log::info!("exporting database tables to {filename}"); - for result in rtx - .open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)? - .iter()? - { - let (number, id) = result?; + let inscription_entries = rtx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; + + for result in rtx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)?.iter()? { + let (_number, id) = result?; let inscription_id = InscriptionId::load(*id.value()); let satpoint = self @@ -468,7 +467,10 @@ impl Index { write!( writer, "{}\t{}\t{}", - number.value(), + inscription_entries + .get(&id.value())? + .map(|entry| InscriptionEntry::load(entry.value()).inscription_number) + .unwrap(), inscription_id, satpoint )?; @@ -652,7 +654,7 @@ impl Index { pub(crate) fn get_inscription_ids_by_sat(&self, sat: Sat) -> Result> { let rtx = &self.database.begin_read()?; - let mut ids = rtx + let ids = rtx .open_multimap_table(SAT_TO_INSCRIPTION_ID)? .get(&sat.n())? .map(|result| { @@ -663,27 +665,38 @@ impl Index { .collect::>>()?; if ids.len() > 1 { - let re_id_to_seq_num = rtx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER)?; - ids.sort_by_key( - |inscription_id| match re_id_to_seq_num.get(&inscription_id.store()) { - Ok(Some(num)) => num.value() + 1, // remove at next index refactor - _ => 0, - }, - ); - } + let inscription_id_to_entry = rtx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; - Ok(ids) + let mut seq_nums = Vec::new(); + for id in &ids { + seq_nums.push( + InscriptionEntry::load(inscription_id_to_entry.get(&id.store())?.unwrap().value()) + .sequence_number, + ) + } + + let mut ids = seq_nums + .into_iter() + .zip(ids) + .collect::>(); + + ids.sort_by_key(|(sequence_number, _)| *sequence_number); + + Ok(ids.into_iter().map(|(_, id)| id).collect()) + } else { + Ok(ids) + } } - pub(crate) fn get_inscription_id_by_inscription_number( + pub(crate) fn get_inscription_id_by_sequence_number( &self, - n: i64, + n: u64, ) -> Result> { Ok( self .database .begin_read()? - .open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)? + .open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)? .get(&n)? .map(|id| Entry::load(*id.value())), ) @@ -730,9 +743,9 @@ impl Index { ) -> 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)?; + let inscription_id_to_entry = rtx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; - Self::inscriptions_on_output_ordered(&re_id_to_seq_num, &sat_to_id, outpoint) + Self::inscriptions_on_output_ordered(&inscription_id_to_entry, &sat_to_id, outpoint) } pub(crate) fn get_inscriptions_on_output( @@ -952,19 +965,18 @@ impl Index { pub(crate) fn get_latest_inscriptions_with_prev_and_next( &self, n: usize, - from: Option, - ) -> Result<(Vec, Option, Option, i64, i64)> { + from: Option, + ) -> Result<(Vec, Option, Option, u64, u64)> { let rtx = self.database.begin_read()?; - let inscription_number_to_inscription_id = - rtx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?; + let sequence_number_to_inscription_id = rtx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)?; - let highest = match inscription_number_to_inscription_id.iter()?.next_back() { + let highest = match sequence_number_to_inscription_id.iter()?.next_back() { Some(Ok((number, _id))) => number.value(), Some(Err(_)) | None => return Ok(Default::default()), }; - let lowest = match inscription_number_to_inscription_id.iter()?.next() { + let lowest = match sequence_number_to_inscription_id.iter()?.next() { Some(Ok((number, _id))) => number.value(), Some(Err(_)) | None => return Ok(Default::default()), }; @@ -972,9 +984,7 @@ impl Index { let from = from.unwrap_or(highest); let prev = if let Some(prev) = from.checked_sub(n.try_into()?) { - inscription_number_to_inscription_id - .get(&prev)? - .map(|_| prev) + sequence_number_to_inscription_id.get(&prev)?.map(|_| prev) } else { None }; @@ -990,7 +1000,7 @@ impl Index { None }; - let inscriptions = inscription_number_to_inscription_id + let inscriptions = sequence_number_to_inscription_id .range(..=from)? .rev() .take(n) @@ -1003,34 +1013,30 @@ impl Index { pub(crate) fn get_inscriptions_in_block(&self, block_height: u64) -> Result> { let rtx = self.database.begin_read()?; - let height_to_last_inscription_number = rtx.open_table(HEIGHT_TO_LAST_INSCRIPTION_NUMBER)?; - let inscription_id_by_number = rtx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?; - - let block_inscriptions = match ( - height_to_last_inscription_number - .get(block_height.saturating_sub(1))? - .map(|ag| ag.value()) - .unwrap_or((0, -1)), - height_to_last_inscription_number - .get(&block_height)? - .map(|ag| ag.value()), - ) { - ((oldest_blessed, oldest_cursed), Some((newest_blessed, newest_cursed))) => { - ((newest_cursed + 1)..=oldest_cursed) - .chain(oldest_blessed..newest_blessed) - .map(|num| match inscription_id_by_number.get(&num) { - Ok(Some(inscription_id)) => Ok(InscriptionId::load(*inscription_id.value())), - Ok(None) => Err(anyhow!( - "could not find inscription for inscription number {num}" - )), - Err(err) => Err(anyhow!(err)), - }) - .collect::>>()? - } - _ => Vec::new(), + let height_to_last_sequence_number = rtx.open_table(HEIGHT_TO_LAST_SEQUENCE_NUMBER)?; + let sequence_number_to_inscription_id = rtx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)?; + + let Some(newest_sequence_number) = height_to_last_sequence_number + .get(&block_height)? + .map(|ag| ag.value()) + else { + return Ok(Vec::new()); }; - Ok(block_inscriptions) + let oldest_sequence_number = height_to_last_sequence_number + .get(block_height.saturating_sub(1))? + .map(|ag| ag.value()) + .unwrap_or(0); + + (oldest_sequence_number..newest_sequence_number) + .map(|num| match sequence_number_to_inscription_id.get(&num) { + Ok(Some(inscription_id)) => Ok(InscriptionId::load(*inscription_id.value())), + Ok(None) => Err(anyhow!( + "could not find inscription for inscription number {num}" + )), + Err(err) => Err(anyhow!(err)), + }) + .collect::>>() } pub(crate) fn get_highest_paying_inscriptions_in_block( @@ -1064,12 +1070,12 @@ impl Index { )) } - pub(crate) fn get_feed_inscriptions(&self, n: usize) -> Result> { + pub(crate) fn get_feed_inscriptions(&self, n: usize) -> Result> { Ok( self .database .begin_read()? - .open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)? + .open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)? .iter()? .rev() .take(n) @@ -1180,7 +1186,7 @@ impl Index { .is_none()); for range in rtx - .open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID) + .open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID) .unwrap() .iter() .into_iter() @@ -1252,7 +1258,7 @@ impl Index { } fn inscriptions_on_output_ordered<'a: 'tx, 'tx>( - re_id_to_seq_num: &'a impl ReadableTable<&'static InscriptionIdValue, u64>, + inscription_id_to_entry: &'a impl ReadableTable<&'static InscriptionIdValue, InscriptionEntryValue>, satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>, outpoint: OutPoint, ) -> Result> { @@ -1264,10 +1270,13 @@ impl Index { } result.sort_by_key(|(_satpoint, inscription_id)| { - match re_id_to_seq_num.get(&inscription_id.store()) { - Ok(Some(num)) => num.value() + 1, // remove at next index refactor - Ok(None) => 0, - _ => 0, + match inscription_id_to_entry + .get(&inscription_id.store()) + .unwrap() + .map(|entry| InscriptionEntry::load(entry.value())) + { + Some(entry) => entry.sequence_number + 1, // remove at next index refactor + None => 0, } }); @@ -2707,7 +2716,7 @@ mod tests { .get_inscription_entry(inscription_id) .unwrap() .unwrap() - .number, + .inscription_number, -1 ); } @@ -2751,7 +2760,7 @@ mod tests { .get_inscription_entry(inscription_id) .unwrap() .unwrap() - .number, + .inscription_number, 0 ); } @@ -2797,7 +2806,7 @@ mod tests { .get_inscription_entry(second_inscription_id) .unwrap() .unwrap() - .number, + .inscription_number, -1 ); } @@ -2860,7 +2869,7 @@ mod tests { .get_inscription_entry(first) .unwrap() .unwrap() - .number, + .inscription_number, 0 ); @@ -2870,7 +2879,7 @@ mod tests { .get_inscription_entry(second) .unwrap() .unwrap() - .number, + .inscription_number, -1 ); @@ -2880,7 +2889,7 @@ mod tests { .get_inscription_entry(third) .unwrap() .unwrap() - .number, + .inscription_number, -2 ); } @@ -2965,7 +2974,7 @@ mod tests { .get_inscription_entry(first) .unwrap() .unwrap() - .number, + .inscription_number, 0 ); @@ -2975,7 +2984,7 @@ mod tests { .get_inscription_entry(second) .unwrap() .unwrap() - .number, + .inscription_number, -1 ); @@ -2985,7 +2994,7 @@ mod tests { .get_inscription_entry(third) .unwrap() .unwrap() - .number, + .inscription_number, -2 ); } @@ -3075,7 +3084,7 @@ mod tests { .get_inscription_entry(first) .unwrap() .unwrap() - .number, + .inscription_number, 0 ); @@ -3085,7 +3094,7 @@ mod tests { .get_inscription_entry(fourth) .unwrap() .unwrap() - .number, + .inscription_number, -3 ); @@ -3095,7 +3104,7 @@ mod tests { .get_inscription_entry(ninth) .unwrap() .unwrap() - .number, + .inscription_number, -8 ); } @@ -3207,7 +3216,7 @@ mod tests { .get_inscription_entry(cursed) .unwrap() .unwrap() - .number, + .inscription_number, -1 ); @@ -3243,7 +3252,7 @@ mod tests { .get_inscription_entry(reinscription_on_cursed) .unwrap() .unwrap() - .number, + .inscription_number, 1 ); } @@ -3288,7 +3297,7 @@ mod tests { .get_inscription_entry(cursed) .unwrap() .unwrap() - .number, + .inscription_number, -1 ); @@ -3324,7 +3333,7 @@ mod tests { .get_inscription_entry(reinscription_on_cursed) .unwrap() .unwrap() - .number, + .inscription_number, 1 ); @@ -3360,7 +3369,7 @@ mod tests { .get_inscription_entry(second_reinscription_on_cursed) .unwrap() .unwrap() - .number, + .inscription_number, -2 ); @@ -3442,52 +3451,6 @@ mod tests { } } - #[test] - fn reinscriptions_sequence_numbers_are_tracked_correctly() { - for context in Context::configurations() { - context.mine_blocks(1); - - let mut inscription_ids = vec![]; - for i in 1..=5 { - let txid = context.rpc_server.broadcast_tx(TransactionTemplate { - inputs: &[( - i, - if i == 1 { 0 } else { 1 }, - 0, - inscription("text/plain;charset=utf-8", &format!("hello {}", i)).to_witness(), - )], // for the first inscription use coinbase, otherwise use the previous tx - ..Default::default() - }); - - inscription_ids.push(InscriptionId { txid, index: 0 }); - - context.mine_blocks(1); - } - - let rtx = context.index.database.begin_read().unwrap(); - let re_id_to_seq_num = rtx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER).unwrap(); - - for (index, id) in inscription_ids.iter().enumerate() { - match re_id_to_seq_num.get(&id.store()) { - Ok(Some(num)) => { - let sequence_number = num.value() + 1; // remove at next index refactor - assert_eq!( - index as u64, sequence_number, - "sequence number mismatch for {:?}", - id - ); - } - Ok(None) => assert_eq!( - index, 0, - "only first inscription should not have sequence number for {:?}", - id - ), - Err(error) => panic!("Error retrieving sequence number: {}", error), - } - } - } - } - #[test] fn reinscriptions_are_ordered_correctly_for_many_outpoints() { for context in Context::configurations() { diff --git a/src/index/entry.rs b/src/index/entry.rs index d857c96b53..5e31fdf871 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -26,22 +26,26 @@ impl Entry for BlockHash { pub(crate) struct InscriptionEntry { pub(crate) fee: u64, pub(crate) height: u64, - pub(crate) number: i64, + pub(crate) inscription_number: i64, + pub(crate) sequence_number: u64, pub(crate) parent: Option, pub(crate) sat: Option, pub(crate) timestamp: u32, } -pub(crate) type InscriptionEntryValue = (u64, u64, i64, ParentValue, u64, u32); +pub(crate) type InscriptionEntryValue = (u64, u64, i64, u64, ParentValue, u64, u32); impl Entry for InscriptionEntry { type Value = InscriptionEntryValue; - fn load((fee, height, number, parent, sat, timestamp): InscriptionEntryValue) -> Self { + fn load( + (fee, height, inscription_number, sequence_number, parent, sat, timestamp): InscriptionEntryValue, + ) -> Self { Self { fee, height, - number, + inscription_number, + sequence_number, parent: ParentEntry::load(parent), sat: if sat == u64::MAX { None @@ -56,7 +60,8 @@ impl Entry for InscriptionEntry { ( self.fee, self.height, - self.number, + self.inscription_number, + self.sequence_number, self.parent.store(), match self.sat { Some(sat) => sat.n(), diff --git a/src/index/updater.rs b/src/index/updater.rs index 2800e1b466..21a116e1fb 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -378,17 +378,15 @@ impl<'index> Updater<'_> { } let mut height_to_block_hash = wtx.open_table(HEIGHT_TO_BLOCK_HASH)?; - let mut height_to_last_inscription_number = - wtx.open_table(HEIGHT_TO_LAST_INSCRIPTION_NUMBER)?; + let mut height_to_last_sequence_number = wtx.open_table(HEIGHT_TO_LAST_SEQUENCE_NUMBER)?; let mut inscription_id_to_inscription_entry = wtx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; 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 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 inscription_id_to_children = wtx.open_multimap_table(INSCRIPTION_ID_TO_CHILDREN)?; let mut satpoint_to_inscription_id = wtx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?; + let mut sequence_number_to_inscription_id = + wtx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)?; let mut statistic_to_count = wtx.open_table(STATISTIC_TO_COUNT)?; let mut lost_sats = statistic_to_count @@ -396,6 +394,16 @@ impl<'index> Updater<'_> { .map(|lost_sats| lost_sats.value()) .unwrap_or(0); + let cursed_inscription_count = statistic_to_count + .get(&Statistic::CursedInscriptions.key())? + .map(|count| count.value()) + .unwrap_or(0); + + let blessed_inscription_count = statistic_to_count + .get(&Statistic::BlessedInscriptions.key())? + .map(|count| count.value()) + .unwrap_or(0); + let unbound_inscriptions = statistic_to_count .get(&Statistic::UnboundInscriptions.key())? .map(|unbound_inscriptions| unbound_inscriptions.value()) @@ -408,9 +416,10 @@ impl<'index> Updater<'_> { value_receiver, &mut inscription_id_to_inscription_entry, lost_sats, - &mut inscription_number_to_inscription_id, + cursed_inscription_count, + blessed_inscription_count, + &mut sequence_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, @@ -515,13 +524,23 @@ impl<'index> Updater<'_> { } self.index_block_inscription_numbers( - &mut height_to_last_inscription_number, + &mut height_to_last_sequence_number, &inscription_updater, index_inscriptions, )?; statistic_to_count.insert(&Statistic::LostSats.key(), &inscription_updater.lost_sats)?; + statistic_to_count.insert( + &Statistic::CursedInscriptions.key(), + &inscription_updater.cursed_inscription_count, + )?; + + statistic_to_count.insert( + &Statistic::BlessedInscriptions.key(), + &inscription_updater.blessed_inscription_count, + )?; + statistic_to_count.insert( &Statistic::UnboundInscriptions.key(), &inscription_updater.unbound_inscriptions, @@ -608,7 +627,7 @@ impl<'index> Updater<'_> { fn index_block_inscription_numbers( &mut self, - height_to_inscription_number: &mut Table, + height_to_sequence_number: &mut Table, inscription_updater: &InscriptionUpdater, index_inscription: bool, ) -> Result { @@ -616,13 +635,7 @@ impl<'index> Updater<'_> { return Ok(()); } - height_to_inscription_number.insert( - &self.height, - ( - inscription_updater.next_number, - inscription_updater.next_cursed_number, - ), - )?; + height_to_sequence_number.insert(&self.height, inscription_updater.next_sequence_number)?; Ok(()) } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index ea08ba41f3..687c1ce47e 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -29,12 +29,12 @@ pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { value_receiver: &'a mut Receiver, id_to_entry: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, InscriptionEntryValue>, pub(super) lost_sats: u64, - pub(super) next_cursed_number: i64, - pub(super) next_number: i64, - number_to_id: &'a mut Table<'db, 'tx, i64, &'static InscriptionIdValue>, + pub(super) cursed_inscription_count: u64, + pub(super) blessed_inscription_count: u64, + pub(super) next_sequence_number: u64, + sequence_number_to_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, outpoint_to_value: &'a mut Table<'db, 'tx, &'static OutPointValue, u64>, reward: u64, - 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>, @@ -56,9 +56,10 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { value_receiver: &'a mut Receiver, id_to_entry: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, InscriptionEntryValue>, lost_sats: u64, - number_to_id: &'a mut Table<'db, 'tx, i64, &'static InscriptionIdValue>, + cursed_inscription_count: u64, + blessed_inscription_count: u64, + sequence_number_to_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, outpoint_to_value: &'a mut Table<'db, 'tx, &'static OutPointValue, u64>, - 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, @@ -70,14 +71,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { unbound_inscriptions: u64, value_cache: &'a mut HashMap, ) -> Result { - let next_cursed_number = number_to_id - .iter()? - .next() - .and_then(|result| result.ok()) - .map(|(number, _id)| number.value() - 1) - .unwrap_or(-1); - - let next_number = number_to_id + let next_sequence_number = sequence_number_to_id .iter()? .next_back() .and_then(|result| result.ok()) @@ -92,12 +86,12 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { value_receiver, id_to_entry, lost_sats, - next_cursed_number, - next_number, - number_to_id, + cursed_inscription_count, + blessed_inscription_count, + next_sequence_number, + sequence_number_to_id, outpoint_to_value, reward: Height(height).subsidy(), - reinscription_id_to_seq_num, sat_to_inscription_id, satpoint_to_id, timestamp, @@ -127,7 +121,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { // 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.id_to_entry, self.satpoint_to_id, tx_in.previous_output, )? { @@ -184,15 +178,11 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } else if inscription.tx_in_offset != 0 { Some(Curse::NotAtOffsetZero) } else if inscribed_offsets.contains_key(&offset) { - let seq_num = self.reinscription_id_to_seq_num.len()?; + let seq_num = self.id_to_entry.len()?; 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)?; + log::info!("processing reinscription {inscription_id} on sat {:?}: sequence number {seq_num}, inscribed offsets {:?}", sat, inscribed_offsets); Some(Curse::Reinscription) } else { @@ -215,7 +205,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.inscription_number < 0 }), Err(_) => None, } @@ -417,19 +407,25 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { parent, unbound, } => { - let number = if cursed { - let next_cursed_number = self.next_cursed_number; - self.next_cursed_number -= 1; + let inscription_number = if cursed { + let number: i64 = self.cursed_inscription_count.try_into().unwrap(); + self.cursed_inscription_count += 1; - next_cursed_number + // because cursed numbers start at -1 + -(number + 1) } else { - let next_number = self.next_number; - self.next_number += 1; + let number: i64 = self.blessed_inscription_count.try_into().unwrap(); + self.blessed_inscription_count += 1; - next_number + number }; - self.number_to_id.insert(number, &inscription_id)?; + let sequence_number = self.next_sequence_number; + self.next_sequence_number += 1; + + self + .sequence_number_to_id + .insert(sequence_number, &inscription_id)?; let sat = if unbound { None @@ -446,7 +442,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { &InscriptionEntry { fee, height: self.height, - number, + inscription_number, + sequence_number, parent, sat, timestamp: self.timestamp, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 006c0df54f..d765a514ea 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -711,7 +711,7 @@ impl Server { let chain = page_config.chain; match chain { - Chain::Mainnet => builder.title("Inscriptions"), + Chain::Mainnet => builder.title("Inscriptions".to_string()), _ => builder.title(format!("Inscriptions – {chain:?}")), }; @@ -720,8 +720,8 @@ impl Server { for (number, id) in index.get_feed_inscriptions(300)? { builder.item( rss::ItemBuilder::default() - .title(format!("Inscription {number}")) - .link(format!("/inscription/{id}")) + .title(Some(format!("Inscription {number}"))) + .link(Some(format!("/inscription/{id}"))) .guid(Some(rss::Guid { value: format!("/inscription/{id}"), permalink: true, @@ -994,9 +994,13 @@ impl Server { ) }; - let previous = index.get_inscription_id_by_inscription_number(entry.number - 1)?; + let previous = if let Some(n) = entry.sequence_number.checked_sub(1) { + index.get_inscription_id_by_sequence_number(n)? + } else { + None + }; - let next = index.get_inscription_id_by_inscription_number(entry.number + 1)?; + let next = index.get_inscription_id_by_sequence_number(entry.sequence_number + 1)?; let children = index.get_children_by_inscription_id(inscription_id)?; @@ -1010,7 +1014,7 @@ impl Server { inscription_id, entry.parent, next, - entry.number, + entry.inscription_number, output, previous, entry.sat, @@ -1027,7 +1031,7 @@ impl Server { inscription, inscription_id, next, - number: entry.number, + inscription_number: entry.inscription_number, output, parent: entry.parent, previous, @@ -1088,7 +1092,7 @@ impl Server { async fn inscriptions_from( Extension(page_config): Extension>, Extension(index): Extension>, - Path(from): Path, + Path(from): Path, accept_json: AcceptJson, ) -> ServerResult { Self::inscriptions_inner(page_config, index, Some(from), 100, accept_json).await @@ -1097,7 +1101,7 @@ impl Server { async fn inscriptions_from_n( Extension(page_config): Extension>, Extension(index): Extension>, - Path((from, n)): Path<(i64, usize)>, + Path((from, n)): Path<(u64, usize)>, accept_json: AcceptJson, ) -> ServerResult { Self::inscriptions_inner(page_config, index, Some(from), n, accept_json).await @@ -1106,7 +1110,7 @@ impl Server { async fn inscriptions_inner( page_config: Arc, index: Arc, - from: Option, + from: Option, n: usize, accept_json: AcceptJson, ) -> ServerResult { @@ -1917,7 +1921,7 @@ mod tests { } #[test] - fn unbound_output_recieves_unbound_inscriptions() { + fn unbound_output_receives_unbound_inscriptions() { let server = TestServer::new_with_regtest(); server.mine_blocks(1); diff --git a/src/templates/inscription.rs b/src/templates/inscription.rs index 6fde60382b..4777aaa074 100644 --- a/src/templates/inscription.rs +++ b/src/templates/inscription.rs @@ -9,7 +9,7 @@ pub(crate) struct InscriptionHtml { pub(crate) inscription: Inscription, pub(crate) inscription_id: InscriptionId, pub(crate) next: Option, - pub(crate) number: i64, + pub(crate) inscription_number: i64, pub(crate) output: Option, pub(crate) parent: Option, pub(crate) previous: Option, @@ -28,7 +28,7 @@ pub struct InscriptionJson { pub genesis_height: u64, pub inscription_id: InscriptionId, pub next: Option, - pub number: i64, + pub inscription_number: i64, pub output_value: Option, pub parent: Option, pub previous: Option, @@ -47,7 +47,7 @@ impl InscriptionJson { inscription_id: InscriptionId, parent: Option, next: Option, - number: i64, + inscription_number: i64, output: Option, previous: Option, sat: Option, @@ -57,7 +57,7 @@ impl InscriptionJson { Self { inscription_id, children, - number, + inscription_number, genesis_height, parent, genesis_fee, @@ -79,7 +79,7 @@ impl InscriptionJson { impl PageContent for InscriptionHtml { fn title(&self) -> String { - format!("Inscription {}", self.number) + format!("Inscription {}", self.inscription_number) } fn preview_image_url(&self) -> Option> { @@ -103,7 +103,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), inscription_id: inscription_id(1), next: None, - number: 1, + inscription_number: 1, output: None, previous: None, sat: None, @@ -160,7 +160,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), inscription_id: inscription_id(1), next: None, - number: 1, + inscription_number: 1, output: Some(tx_out(1, address())), previous: None, sat: None, @@ -199,7 +199,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), inscription_id: inscription_id(1), next: None, - number: 1, + inscription_number: 1, output: Some(tx_out(1, address())), previous: None, sat: Some(Sat(1)), @@ -233,7 +233,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), inscription_id: inscription_id(2), next: Some(inscription_id(3)), - number: 1, + inscription_number: 1, output: Some(tx_out(1, address())), previous: Some(inscription_id(1)), sat: None, @@ -265,7 +265,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), inscription_id: inscription_id(2), next: None, - number: -1, + inscription_number: -1, output: Some(tx_out(1, address())), previous: None, sat: None, @@ -303,7 +303,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), inscription_id: inscription_id(1), next: None, - number: 1, + inscription_number: 1, output: None, previous: None, sat: None, @@ -362,7 +362,7 @@ mod tests { inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"), inscription_id: inscription_id(1), next: None, - number: 1, + inscription_number: 1, output: None, previous: None, sat: None, diff --git a/src/templates/inscriptions.rs b/src/templates/inscriptions.rs index eb2a0e091f..19218c0d5b 100644 --- a/src/templates/inscriptions.rs +++ b/src/templates/inscriptions.rs @@ -3,26 +3,26 @@ use super::*; #[derive(Boilerplate)] pub(crate) struct InscriptionsHtml { pub(crate) inscriptions: Vec, - pub(crate) prev: Option, - pub(crate) next: Option, + pub(crate) prev: Option, + pub(crate) next: Option, } #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct InscriptionsJson { pub inscriptions: Vec, - pub prev: Option, - pub next: Option, - pub lowest: Option, - pub highest: Option, + pub prev: Option, + pub next: Option, + pub lowest: Option, + pub highest: Option, } impl InscriptionsJson { pub fn new( inscriptions: Vec, - prev: Option, - next: Option, - lowest: Option, - highest: Option, + prev: Option, + next: Option, + lowest: Option, + highest: Option, ) -> Self { Self { inscriptions, diff --git a/templates/inscription.html b/templates/inscription.html index 429d8c16ae..56b4839da1 100644 --- a/templates/inscription.html +++ b/templates/inscription.html @@ -1,7 +1,7 @@ -%% if self.number >= 0 { -

Inscription {{ self.number }}

+%% if self.inscription_number >= 0 { +

Inscription {{ self.inscription_number }}

%% } else { -

Inscription {{ self.number }} (unstable)

+

Inscription {{ self.inscription_number }} (unstable)

%% }
%% if let Some(previous) = self.previous { diff --git a/tests/json_api.rs b/tests/json_api.rs index 52cc62801c..662ff733d1 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -155,7 +155,7 @@ fn get_inscription() { parent: None, children: Vec::new(), inscription_id, - number: 0, + inscription_number: 0, genesis_height: 2, genesis_fee: 138, output_value: Some(10000), @@ -171,13 +171,10 @@ fn get_inscription() { ) } -fn create_210_inscriptions( - rpc_server: &test_bitcoincore_rpc::Handle, -) -> (Vec, Vec) { +fn create_210_inscriptions(rpc_server: &test_bitcoincore_rpc::Handle) -> Vec { let witness = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); - let mut blessed_inscriptions = Vec::new(); - let mut cursed_inscriptions = Vec::new(); + let mut inscriptions = Vec::new(); // Create 150 inscriptions, 50 non-cursed and 100 cursed for i in 0..50 { @@ -194,9 +191,9 @@ fn create_210_inscriptions( ..Default::default() }); - blessed_inscriptions.push(InscriptionId { txid, index: 0 }); - cursed_inscriptions.push(InscriptionId { txid, index: 1 }); - cursed_inscriptions.push(InscriptionId { txid, index: 2 }); + inscriptions.push(InscriptionId { txid, index: 0 }); + inscriptions.push(InscriptionId { txid, index: 1 }); + inscriptions.push(InscriptionId { txid, index: 2 }); } rpc_server.mine_blocks(1); @@ -208,7 +205,7 @@ fn create_210_inscriptions( .rpc_server(rpc_server) .run_and_deserialize_output(); rpc_server.mine_blocks(1); - blessed_inscriptions.push(InscriptionId { + inscriptions.push(InscriptionId { txid: reveal, index: 0, }); @@ -216,7 +213,7 @@ fn create_210_inscriptions( rpc_server.mine_blocks(1); - (blessed_inscriptions, cursed_inscriptions) + inscriptions } #[test] @@ -224,7 +221,7 @@ fn get_inscriptions() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - let (blessed_inscriptions, cursed_inscriptions) = create_210_inscriptions(&rpc_server); + let inscriptions = create_210_inscriptions(&rpc_server); let server = TestServer::spawn_with_args(&rpc_server, &["--index-sats", "--enable-json-api"]); @@ -235,55 +232,25 @@ fn get_inscriptions() { // 100 latest (blessed) inscriptions assert_eq!(inscriptions_json.inscriptions.len(), 100); - pretty_assert_eq!( - inscriptions_json, - InscriptionsJson { - inscriptions: blessed_inscriptions[10..110] - .iter() - .cloned() - .rev() - .collect(), - prev: Some(9), - next: None, - lowest: Some(-100), - highest: Some(109), - } - ); // get all inscriptions - let response = server.json_request(format!("/inscriptions/{}/{}", 200, 400)); + let response = server.json_request(format!("/inscriptions/{}/{}", 500, 400)); assert_eq!(response.status(), StatusCode::OK); let inscriptions_json: InscriptionsJson = serde_json::from_str(&response.text().unwrap()).unwrap(); - assert_eq!( - inscriptions_json.inscriptions.len(), - blessed_inscriptions.len() + cursed_inscriptions.len() - ); + assert_eq!(inscriptions_json.inscriptions.len(), inscriptions.len()); pretty_assert_eq!( inscriptions_json.inscriptions, - blessed_inscriptions - .iter() - .cloned() - .rev() - .chain(cursed_inscriptions.clone()) - .collect::>() + inscriptions.iter().cloned().rev().collect::>() ); - // iterate over all inscriptions 1 by 1 - let all_inscriptions = cursed_inscriptions - .clone() - .iter() - .cloned() - .rev() - .chain(blessed_inscriptions.clone()) - .collect::>(); // from lowest to highest inscription number - let (lowest, highest) = ( inscriptions_json.lowest.unwrap(), inscriptions_json.highest.unwrap(), ); + for i in lowest..=highest { let response = server.json_request(format!("/inscriptions/{}/1", i)); assert_eq!(response.status(), StatusCode::OK); @@ -294,7 +261,7 @@ fn get_inscriptions() { assert_eq!(inscriptions_json.inscriptions.len(), 1); assert_eq!( inscriptions_json.inscriptions[0], - all_inscriptions[(i - lowest) as usize] + inscriptions[(i - lowest) as usize] ); let response = server.json_request(format!( @@ -310,7 +277,6 @@ fn get_inscriptions() { inscription_json.inscription_id, inscriptions_json.inscriptions[0] ); - assert_eq!(inscription_json.number, i); } } @@ -334,9 +300,18 @@ fn get_inscriptions_in_block() { rpc_server.mine_blocks(1); - for _ in 0..10 { - inscribe(&rpc_server); - } + let _ = rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(4, 0, 0, envelope.clone()), (5, 0, 0, envelope.clone())], + ..Default::default() + }); + + rpc_server.mine_blocks(1); + + let _ = rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(6, 0, 0, envelope.clone())], + ..Default::default() + }); + rpc_server.mine_blocks(1); let server = TestServer::spawn_with_args( @@ -359,9 +334,9 @@ fn get_inscriptions_in_block() { pretty_assert_eq!( inscriptions_json.inscriptions, vec![ - InscriptionId { txid, index: 2 }, + InscriptionId { txid, index: 0 }, InscriptionId { txid, index: 1 }, - InscriptionId { txid, index: 0 } + InscriptionId { txid, index: 2 }, ] ); } @@ -406,8 +381,8 @@ fn get_output() { ],), inscriptions: vec![ InscriptionId { txid, index: 0 }, + InscriptionId { txid, index: 1 }, InscriptionId { txid, index: 2 }, - InscriptionId { txid, index: 1 } ] } ); diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index e485dd3a3c..95b70ac74a 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -256,12 +256,12 @@ fn splitting_merged_inscriptions_is_possible() { }, InscriptionId { txid: reveal_txid, - index: 2 + index: 1 }, InscriptionId { txid: reveal_txid, - index: 1 - } + index: 2 + }, ] } );