From 8a6b9f06fa3617cdb97b018ca5b1a3593cc52f6d Mon Sep 17 00:00:00 2001 From: ordinally <11798624+veryordinally@users.noreply.github.com> Date: Mon, 17 Jul 2023 11:20:23 +0200 Subject: [PATCH] Fix ordering for reinscriptions and show all reinscriptions for sat (#2279) --- src/index.rs | 159 +++++++++++++++++++---- src/index/updater/inscription_updater.rs | 8 +- src/subcommand/server.rs | 2 +- src/templates/sat.rs | 46 +++++-- templates/sat.html | 9 +- 5 files changed, 180 insertions(+), 44 deletions(-) diff --git a/src/index.rs b/src/index.rs index 619e48d6bd..8962f31202 100644 --- a/src/index.rs +++ b/src/index.rs @@ -596,17 +596,30 @@ impl Index { self.client.get_block(&hash).into_option() } - pub(crate) fn get_inscription_id_by_sat(&self, sat: Sat) -> Result> { - Ok( - self - .database - .begin_read()? - .open_multimap_table(SAT_TO_INSCRIPTION_ID)? - .get(&sat.n())? - .next() - .and_then(|result| result.ok()) - .map(|inscription_id| Entry::load(*inscription_id.value())), - ) + pub(crate) fn get_inscription_ids_by_sat(&self, sat: Sat) -> Result> { + let rtx = &self.database.begin_read()?; + + let mut ids = rtx + .open_multimap_table(SAT_TO_INSCRIPTION_ID)? + .get(&sat.n())? + .filter_map(|result| { + result + .ok() + .map(|inscription_id| InscriptionId::load(*inscription_id.value())) + }) + .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, + }, + ); + } + + Ok(ids) } pub(crate) fn get_inscription_id_by_inscription_number( @@ -658,20 +671,27 @@ impl Index { })) } + pub(crate) fn get_inscriptions_on_output_with_satpoints( + &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)?; + + Self::inscriptions_on_output_ordered(&re_id_to_seq_num, &sat_to_id, outpoint) + } + pub(crate) fn get_inscriptions_on_output( &self, outpoint: OutPoint, ) -> Result> { Ok( - Self::inscriptions_on_output( - &self - .database - .begin_read()? - .open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?, - outpoint, - )? - .map(|(_satpoint, inscription_id)| inscription_id) - .collect(), + self + .get_inscriptions_on_output_with_satpoints(outpoint)? + .iter() + .map(|(_satpoint, inscription_id)| *inscription_id) + .collect(), ) } @@ -918,7 +938,7 @@ impl Index { let id = id_result?; result.insert(Entry::load(*satpoint.value()), Entry::load(*id.value())); } - if result.len() == n.unwrap_or(usize::MAX) { + if result.len() >= n.unwrap_or(usize::MAX) { break; } } @@ -1086,7 +1106,7 @@ impl Index { } } - fn inscriptions_on_output<'a: 'tx, 'tx>( + fn inscriptions_on_output_unordered<'a: 'tx, 'tx>( satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>, outpoint: OutPoint, ) -> Result + 'tx> { @@ -1120,7 +1140,7 @@ impl Index { satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>, outpoint: OutPoint, ) -> Result> { - let mut result = Self::inscriptions_on_output(satpoint_to_id, outpoint)? + let mut result = Self::inscriptions_on_output_unordered(satpoint_to_id, outpoint)? .collect::>(); if result.len() <= 1 { @@ -1129,7 +1149,7 @@ impl Index { result.sort_by_key(|(_satpoint, inscription_id)| { match re_id_to_seq_num.get(&inscription_id.store()) { - Ok(Some(num)) => num.value(), + Ok(Some(num)) => num.value() + 1, // remove at next index refactor Ok(None) => 0, _ => 0, } @@ -3143,7 +3163,7 @@ mod tests { ], context .index - .get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 }) + .get_inscriptions_on_output_with_satpoints(OutPoint { txid, vout: 0 }) .unwrap() .iter() .map(|(_satpoint, inscription_id)| *inscription_id) @@ -3195,7 +3215,94 @@ mod tests { vec![(location, first), (location, second), (location, third)], context .index - .get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 }) + .get_inscriptions_on_output_with_satpoints(OutPoint { txid, vout: 0 }) + .unwrap() + ) + } + } + + #[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)], // for the first inscription use coinbase, otherwise use the previous tx + witness: inscription("text/plain;charset=utf-8", &format!("hello {}", i)).to_witness(), + ..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() { + context.mine_blocks(1); + + let mut inscription_ids = vec![]; + for i in 1..=21 { + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(i, if i == 1 { 0 } else { 1 }, 0)], // for the first inscription use coinbase, otherwise use the previous tx + witness: inscription("text/plain;charset=utf-8", &format!("hello {}", i)).to_witness(), + ..Default::default() + }); + + inscription_ids.push(InscriptionId { txid, index: 0 }); + + context.mine_blocks(1); + } + + let final_txid = inscription_ids.last().unwrap().txid; + let location = SatPoint { + outpoint: OutPoint { + txid: final_txid, + vout: 0, + }, + offset: 0, + }; + + let expected_result = inscription_ids + .iter() + .map(|id| (location, *id)) + .collect::>(); + + assert_eq!( + expected_result, + context + .index + .get_inscriptions_on_output_with_satpoints(OutPoint { + txid: final_txid, + vout: 0 + }) .unwrap() ) } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index d0d17e1818..fce36e608e 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -169,13 +169,7 @@ 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 - .iter()? - .next_back() - .and_then(|result| result.ok()) - .map(|(_id, number)| number.value() + 1) - .unwrap_or(0); + let seq_num = self.reinscription_id_to_seq_num.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); diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 95b643383a..9dbaf4c3d2 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -402,7 +402,7 @@ impl Server { sat, satpoint, blocktime: index.block_time(sat.height())?, - inscription: index.get_inscription_id_by_sat(sat)?, + inscriptions: index.get_inscription_ids_by_sat(sat)?, } .page(page_config, index.has_sat_index()?), ) diff --git a/src/templates/sat.rs b/src/templates/sat.rs index 8894ca1388..fd1c5bab7d 100644 --- a/src/templates/sat.rs +++ b/src/templates/sat.rs @@ -5,7 +5,7 @@ pub(crate) struct SatHtml { pub(crate) sat: Sat, pub(crate) satpoint: Option, pub(crate) blocktime: Blocktime, - pub(crate) inscription: Option, + pub(crate) inscriptions: Vec, } impl PageContent for SatHtml { @@ -25,7 +25,7 @@ mod tests { sat: Sat(0), satpoint: None, blocktime: Blocktime::confirmed(0), - inscription: None, + inscriptions: Vec::new(), }, "

Sat 0

@@ -58,7 +58,7 @@ mod tests { sat: Sat(2099999997689999), satpoint: None, blocktime: Blocktime::confirmed(0), - inscription: None, + inscriptions: Vec::new(), }, "

Sat 2099999997689999

@@ -91,7 +91,7 @@ mod tests { sat: Sat(1), satpoint: None, blocktime: Blocktime::confirmed(0), - inscription: None, + inscriptions: Vec::new(), }, r"

Sat 1

.*\n.*", ); @@ -104,9 +104,39 @@ mod tests { sat: Sat(0), satpoint: None, blocktime: Blocktime::confirmed(0), - inscription: Some(inscription_id(1)), + inscriptions: vec![inscription_id(1)], }, - r"

Sat 0

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

Sat 0

+ .* +
inscriptions
+
+ .* +
+ .*" + .unindent(), + ); + } + + #[test] + fn sat_with_reinscription() { + assert_regex_match!( + SatHtml { + sat: Sat(0), + satpoint: None, + blocktime: Blocktime::confirmed(0), + inscriptions: vec![inscription_id(1), inscription_id(2)], + }, + " +

Sat 0

+ .* +
inscriptions
+
+ .* + .* +
+ .*" + .unindent(), ); } @@ -117,7 +147,7 @@ mod tests { sat: Sat::LAST, satpoint: None, blocktime: Blocktime::confirmed(0), - inscription: None, + inscriptions: Vec::new(), }, r"

Sat 2099999997689999

.*\nnext.*", ); @@ -130,7 +160,7 @@ mod tests { sat: Sat(0), satpoint: Some(satpoint(1, 0)), blocktime: Blocktime::confirmed(0), - inscription: None, + inscriptions: Vec::new(), }, "

Sat 0

.*
location
1{64}:1:0
.*", ); diff --git a/templates/sat.html b/templates/sat.html index 05177a4ff5..51ac8b3e85 100644 --- a/templates/sat.html +++ b/templates/sat.html @@ -11,8 +11,13 @@

Sat {{ self.sat.n() }}

offset
{{ self.sat.third() }}
rarity
{{ self.sat.rarity() }}
timestamp
{{self.blocktime.suffix()}}
-%% if let Some((inscription)) = &self.inscription { -
inscription
{{ Iframe::thumbnail(*inscription) }}
+%% if !self.inscriptions.is_empty() { +
inscriptions
+
+%% for inscription in &self.inscriptions { + {{Iframe::thumbnail(*inscription)}} +%% } +
%% } %% if let Some(satpoint) = self.satpoint {
location
{{ satpoint }}