From 68158786f06c0a4ad6f56509eaa96012986f7790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20C=C3=A1rdenas?= Date: Fri, 21 Feb 2025 09:31:39 -0600 Subject: [PATCH] fix: display unbound inscription satpoints as all zeros with unbound sequence as offset (#445) * feat: start indexing brc20 balance history * fix: api support * style: revert * remove extra * track unbound seq * test sequence * remove old coinbase calculations --- .../chainhook-sdk/src/indexer/bitcoin/mod.rs | 2 +- components/chainhook-types-rs/src/ordinals.rs | 1 + .../core/meta_protocols/brc20/test_utils.rs | 1 + .../src/core/protocol/inscription_parsing.rs | 1 + .../core/protocol/inscription_sequencing.rs | 181 +++++++++++++++--- .../src/core/protocol/satoshi_tracking.rs | 91 ++------- .../src/core/protocol/sequence_cursor.rs | 49 +++++ .../ordhook-core/src/core/test_builders.rs | 1 + .../src/db/models/db_inscription.rs | 3 + .../src/db/models/db_inscription_recursion.rs | 1 + components/ordhook-core/src/db/ordinals_pg.rs | 20 +- .../V17__unbound_inscription_sequence.sql | 1 + 12 files changed, 247 insertions(+), 105 deletions(-) create mode 100644 migrations/ordinals/V17__unbound_inscription_sequence.sql diff --git a/components/chainhook-sdk/src/indexer/bitcoin/mod.rs b/components/chainhook-sdk/src/indexer/bitcoin/mod.rs index 7a54f6a..4e96960 100644 --- a/components/chainhook-sdk/src/indexer/bitcoin/mod.rs +++ b/components/chainhook-sdk/src/indexer/bitcoin/mod.rs @@ -9,7 +9,7 @@ use bitcoincore_rpc::jsonrpc::error::RpcError; use bitcoincore_rpc_json::GetRawTransactionResultVoutScriptPubKey; use chainhook_types::bitcoin::{OutPoint, TxIn, TxOut}; use chainhook_types::{ - BitcoinBlockData, BitcoinBlockMetadata, BitcoinBlockSignaling, BitcoinNetwork, + BitcoinBlockData, BitcoinBlockMetadata, BitcoinNetwork, BitcoinTransactionData,BitcoinTransactionMetadata, BlockHeader, BlockIdentifier, TransactionIdentifier, }; diff --git a/components/chainhook-types-rs/src/ordinals.rs b/components/chainhook-types-rs/src/ordinals.rs index 9cab2d9..2b901d9 100644 --- a/components/chainhook-types-rs/src/ordinals.rs +++ b/components/chainhook-types-rs/src/ordinals.rs @@ -63,6 +63,7 @@ pub struct OrdinalInscriptionRevealData { pub satpoint_post_inscription: String, pub curse_type: Option, pub charms: u16, + pub unbound_sequence: Option, } impl OrdinalInscriptionNumber { diff --git a/components/ordhook-core/src/core/meta_protocols/brc20/test_utils.rs b/components/ordhook-core/src/core/meta_protocols/brc20/test_utils.rs index 8093fe3..f008e94 100644 --- a/components/ordhook-core/src/core/meta_protocols/brc20/test_utils.rs +++ b/components/ordhook-core/src/core/meta_protocols/brc20/test_utils.rs @@ -89,6 +89,7 @@ impl Brc20RevealBuilder { "9bb2314d666ae0b1db8161cb373fcc1381681f71445c4e0335aa80ea9c37fcdd:0:0".to_string(), curse_type: None, charms: 0, + unbound_sequence: None, } } } diff --git a/components/ordhook-core/src/core/protocol/inscription_parsing.rs b/components/ordhook-core/src/core/protocol/inscription_parsing.rs index e3c27e7..5824c92 100644 --- a/components/ordhook-core/src/core/protocol/inscription_parsing.rs +++ b/components/ordhook-core/src/core/protocol/inscription_parsing.rs @@ -103,6 +103,7 @@ pub fn parse_inscriptions_from_witness( satpoint_post_inscription: format!(""), curse_type, charms: 0, + unbound_sequence: None, }; inscriptions.push((reveal_data, envelope.payload)); } diff --git a/components/ordhook-core/src/core/protocol/inscription_sequencing.rs b/components/ordhook-core/src/core/protocol/inscription_sequencing.rs index 6e86f77..1daa682 100644 --- a/components/ordhook-core/src/core/protocol/inscription_sequencing.rs +++ b/components/ordhook-core/src/core/protocol/inscription_sequencing.rs @@ -16,6 +16,7 @@ use dashmap::DashMap; use deadpool_postgres::Transaction; use fxhash::FxHasher; +use crate::core::protocol::satoshi_tracking::UNBOUND_INSCRIPTION_SATPOINT; use crate::{ config::Config, core::resolve_absolute_pointer, @@ -23,7 +24,7 @@ use crate::{ try_debug, try_error, try_info, utils::format_inscription_id, }; -use ord::{charm::Charm, height::Height, sat::Sat}; +use ord::{charm::Charm, sat::Sat}; use std::sync::mpsc::channel; @@ -401,12 +402,9 @@ pub async fn update_block_inscriptions_with_consensus_sequence_data( // Check if we've previously inscribed over any satoshi being inscribed to in this new block. This would be a reinscription. let mut reinscriptions_data = ordinals_pg::get_reinscriptions_for_block(inscriptions_data, db_tx).await?; - // Keep a reference of inscribed satoshis that fall outside of this block's total sats. These would be unbound inscriptions. + // Keep a reference of inscribed satoshis that will go towards miner fees. These would be unbound inscriptions. let mut sat_overflows = VecDeque::new(); let network = get_bitcoin_network(&block.metadata.network); - let coinbase_subsidy = Height(block.block_identifier.index as u32).subsidy(); - let coinbase_tx = &block.transactions[0].clone(); - let mut cumulated_fees = 0u64; for (tx_index, tx) in block.transactions.iter_mut().enumerate() { update_tx_inscriptions_with_consensus_sequence_data( @@ -416,9 +414,6 @@ pub async fn update_block_inscriptions_with_consensus_sequence_data( sequence_cursor, &network, inscriptions_data, - coinbase_tx, - coinbase_subsidy, - &mut cumulated_fees, &mut sat_overflows, &mut reinscriptions_data, db_tx, @@ -427,19 +422,28 @@ pub async fn update_block_inscriptions_with_consensus_sequence_data( .await?; } + // Assign inscription numbers to remaining unbound inscriptions. while let Some((tx_index, op_index)) = sat_overflows.pop_front() { let OrdinalOperation::InscriptionRevealed(ref mut inscription_data) = block.transactions[tx_index].metadata.ordinal_operations[op_index] else { continue; }; + let is_cursed = inscription_data.curse_type.is_some(); let inscription_number = sequence_cursor .pick_next(is_cursed, block.block_identifier.index, &network, db_tx) .await?; inscription_data.inscription_number = inscription_number; - sequence_cursor.increment(is_cursed, db_tx).await?; + + // Also assign an unbound sequence number and set outpoint to all zeros, just like `ord`. + let unbound_sequence = sequence_cursor.increment_unbound(db_tx).await?; + inscription_data.satpoint_post_inscription = + format!("{UNBOUND_INSCRIPTION_SATPOINT}:{unbound_sequence}"); + inscription_data.ordinal_offset = unbound_sequence as u64; + inscription_data.unbound_sequence = Some(unbound_sequence); + try_info!( ctx, "Unbound inscription {} (#{}) detected on Satoshi {} (block #{}, {} transfers)", @@ -464,9 +468,6 @@ async fn update_tx_inscriptions_with_consensus_sequence_data( sequence_cursor: &mut SequenceCursor, network: &Network, inscriptions_data: &mut BTreeMap<(TransactionIdentifier, usize, u64), TraversalResult>, - coinbase_tx: &BitcoinTransactionData, - coinbase_subsidy: u64, - cumulated_fees: &mut u64, sats_overflows: &mut VecDeque<(usize, usize)>, reinscriptions_data: &mut HashMap, db_tx: &Transaction<'_>, @@ -559,16 +560,8 @@ async fn update_tx_inscriptions_with_consensus_sequence_data( } } - let (destination, satpoint_post_transfer, output_value) = compute_satpoint_post_transfer( - &&*tx, - input_index, - relative_offset, - network, - coinbase_tx, - coinbase_subsidy, - cumulated_fees, - ctx, - ); + let (destination, satpoint_post_transfer, output_value) = + compute_satpoint_post_transfer(&&*tx, input_index, relative_offset, network, ctx); inscription.satpoint_post_inscription = satpoint_post_transfer; inscription_subindex += 1; @@ -637,11 +630,150 @@ mod test { protocol::{satoshi_numbering::TraversalResult, sequence_cursor::SequenceCursor}, test_builders::{TestBlockBuilder, TestTransactionBuilder}, }, - db::{ordinals_pg, pg_reset_db, pg_test_connection, pg_test_connection_pool}, + db::{ + ordinals_pg::{self, insert_block}, + pg_reset_db, pg_test_connection, pg_test_connection_pool, + }, }; use super::update_block_inscriptions_with_consensus_sequence_data; + #[test_case(None => Ok(("0000000000000000000000000000000000000000000000000000000000000000:0:0".into(), Some(0))); "first unbound sequence")] + #[test_case(Some(230) => Ok(("0000000000000000000000000000000000000000000000000000000000000000:0:231".into(), Some(231))); "next unbound sequence")] + #[tokio::test] + async fn unbound_inscription_sequence( + curr_sequence: Option, + ) -> Result<(String, Option), String> { + let ctx = Context::empty(); + let mut sequence_cursor = SequenceCursor::new(); + let mut cache_l1 = BTreeMap::new(); + let tx_id = TransactionIdentifier { + hash: "0xb4722ad74e7092a194e367f2ec0609994ef7a006db4f9b9d055b46cfb6514e06".into(), + }; + let input_index = 1; + + cache_l1.insert( + (tx_id.clone(), input_index, 0), + TraversalResult { + inscription_number: OrdinalInscriptionNumber { + classic: 0, + jubilee: 0, + }, + inscription_input_index: input_index, + transaction_identifier_inscription: tx_id.clone(), + ordinal_number: 817263817263, + transfers: 0, + }, + ); + let mut pg_client = pg_test_connection().await; + ordinals_pg::migrate(&mut pg_client).await?; + let result = { + let mut ord_client = pg_pool_client(&pg_test_connection_pool()).await?; + let client = pg_begin(&mut ord_client).await?; + + if let Some(curr_sequence) = curr_sequence { + // Simulate previous unbound sequence + let mut tx = TestTransactionBuilder::new_with_operation().build(); + if let OrdinalOperation::InscriptionRevealed(data) = + &mut tx.metadata.ordinal_operations[0] + { + data.unbound_sequence = Some(curr_sequence); + }; + let block = TestBlockBuilder::new().transactions(vec![tx]).build(); + insert_block(&block, &client).await?; + } + + // Insert new block + let mut block = TestBlockBuilder::new() + .height(878878) + // Coinbase + .add_transaction(TestTransactionBuilder::new().build()) + .add_transaction( + TestTransactionBuilder::new() + .hash(tx_id.hash.clone()) + // Normal input + .add_input(TxIn { + previous_output: OutPoint { + txid: TransactionIdentifier { hash: "0xf181aa98f2572879bd02278c72c83c7eaac2db82af713d1d239fc41859b2a26e".into() }, + vout: 0, + value: 8000, + block_height: 884200, + }, + script_sig: "0x00".into(), + sequence: 0, + witness: vec!["0x00".into()], + }) + // Goes to fees + .add_input(TxIn { + previous_output: OutPoint { + txid: TransactionIdentifier { hash: "0xf181aa98f2572879bd02278c72c83c7eaac2db82af713d1d239fc41859b2a26e".into() }, + vout: 1, + value: 250, + block_height: 884200, + }, + script_sig: "0x00".into(), + sequence: 0, + witness: vec!["0x00".into()], + }) + .add_output(TxOut { value: 8000, script_pubkey: "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into() }) + .add_ordinal_operation(OrdinalOperation::InscriptionRevealed( + OrdinalInscriptionRevealData { + content_bytes: "0x101010".into(), + content_type: "text/plain".into(), + content_length: 3, + inscription_number: OrdinalInscriptionNumber { + classic: 0, + jubilee: 0, + }, + inscription_fee: 0, + inscription_output_value: 0, + inscription_id: "".into(), + inscription_input_index: input_index, + inscription_pointer: Some(8000), + inscriber_address: Some("bc1pd99n363yjz8gd2zhy7gstsmk4qkdz4t029j44wewhmee3dta429sm5xqrd".into()), + delegate: None, + metaprotocol: None, + metadata: None, + parents: vec![], + ordinal_number: 0, + ordinal_block_height: 0, + ordinal_offset: 0, + tx_index: 1, + transfers_pre_inscription: 0, + satpoint_post_inscription: "".into(), + curse_type: Some(OrdinalInscriptionCurseType::DuplicateField), + charms: 0, + unbound_sequence: None, + }, + )) + .build(), + ) + .build(); + + update_block_inscriptions_with_consensus_sequence_data( + &mut block, + &mut sequence_cursor, + &mut cache_l1, + &client, + &ctx, + ) + .await?; + + let result = &block.transactions[1].metadata.ordinal_operations[0]; + let data = match result { + OrdinalOperation::InscriptionRevealed(data) => data, + _ => unreachable!(), + }; + Ok(( + data.satpoint_post_inscription.clone(), + data.unbound_sequence, + )) + }; + pg_reset_db(&mut pg_client).await?; + + result + } + #[test_case((884207, false, 1262349832364434, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![]); "common sat")] #[test_case((884207, false, 0, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![Charm::Coin, Charm::Mythic, Charm::Palindrome]); "mythic sat")] #[test_case((884207, false, 1050000000000000, "0x5120694b38ea24908e86a857279105c376a82cd1556f51655abb2ebef398b57daa8b".into()) => Ok(vec![Charm::Coin, Charm::Epic]); "epic sat")] @@ -727,6 +859,7 @@ mod test { satpoint_post_inscription: "".into(), curse_type: if cursed { Some(OrdinalInscriptionCurseType::Generic) } else { None }, charms: 0, + unbound_sequence: None, }, )) .build(), @@ -743,12 +876,10 @@ mod test { .await?; let result = &block.transactions[1].metadata.ordinal_operations[0]; - // println!("{:?}", result); let charms = match result { OrdinalOperation::InscriptionRevealed(data) => data.charms, _ => unreachable!(), }; - // println!("{:?}", Charm::charms(charms)); Ok(Charm::charms(charms)) }; pg_reset_db(&mut pg_client).await?; diff --git a/components/ordhook-core/src/core/protocol/satoshi_tracking.rs b/components/ordhook-core/src/core/protocol/satoshi_tracking.rs index 01f0f88..0071fe8 100644 --- a/components/ordhook-core/src/core/protocol/satoshi_tracking.rs +++ b/components/ordhook-core/src/core/protocol/satoshi_tracking.rs @@ -14,10 +14,12 @@ use crate::{ try_info, utils::format_outpoint_to_watch, }; -use ord::height::Height; use super::inscription_sequencing::get_bitcoin_network; +pub const UNBOUND_INSCRIPTION_SATPOINT: &str = + "0000000000000000000000000000000000000000000000000000000000000000:0"; + #[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)] pub struct WatchedSatpoint { pub ordinal_number: u64, @@ -30,7 +32,7 @@ pub fn parse_output_and_offset_from_satpoint( let parts: Vec<&str> = satpoint.split(':').collect(); let tx_id = parts .get(0) - .ok_or("get_output_and_offset_from_satpoint: inscription_id not found")?; + .ok_or("get_output_and_offset_from_satpoint: tx_id not found")?; let output = parts .get(1) .ok_or("get_output_and_offset_from_satpoint: output not found")?; @@ -50,18 +52,12 @@ pub async fn augment_block_with_transfers( ctx: &Context, ) -> Result<(), String> { let network = get_bitcoin_network(&block.metadata.network); - let coinbase_subsidy = Height(block.block_identifier.index as u32).subsidy(); - let coinbase_tx = &block.transactions[0].clone(); - let mut cumulated_fees = 0; for (tx_index, tx) in block.transactions.iter_mut().enumerate() { let _ = augment_transaction_with_ordinal_transfers( tx, tx_index, &block.block_identifier, &network, - &coinbase_tx, - coinbase_subsidy, - &mut cumulated_fees, db_tx, ctx, ) @@ -75,9 +71,6 @@ pub fn compute_satpoint_post_transfer( input_index: usize, relative_pointer_value: u64, network: &Network, - coinbase_tx: &BitcoinTransactionData, - coinbase_subsidy: u64, - cumulated_fees: &mut u64, ctx: &Context, ) -> (OrdinalInscriptionTransferDestination, String, Option) { let inputs: Vec = tx @@ -131,37 +124,11 @@ pub fn compute_satpoint_post_transfer( Some(tx.metadata.outputs[output_index].value), ) } - SatPosition::Fee(offset) => { - // Get Coinbase TX - let total_offset = coinbase_subsidy + *cumulated_fees + offset; - let outputs = coinbase_tx - .metadata - .outputs - .iter() - .map(|o| o.value) - .collect(); - let post_transfer_data = compute_next_satpoint_data( - 0, - &vec![total_offset], - &outputs, - total_offset, - Some(ctx), - ); - - // Identify the correct output - let (output_index, offset) = match post_transfer_data { - SatPosition::Output(pos) => pos, - _ => { - try_info!(ctx, "unable to locate satoshi in coinbase outputs"); - (0, total_offset) - } - }; - - let outpoint = - format_outpoint_to_watch(&coinbase_tx.transaction_identifier, output_index); + SatPosition::Fee(_) => { + // Unbound inscription satpoints will be updated later with an unbound sequence number. ( - outpoint, - offset, + UNBOUND_INSCRIPTION_SATPOINT.into(), + 0, OrdinalInscriptionTransferDestination::SpentInFees, None, ) @@ -181,9 +148,6 @@ pub async fn augment_transaction_with_ordinal_transfers( tx_index: usize, block_identifier: &BlockIdentifier, network: &Network, - coinbase_tx: &BitcoinTransactionData, - coinbase_subsidy: u64, - cumulated_fees: &mut u64, db_tx: &Transaction<'_>, ctx: &Context, ) -> Result, String> { @@ -224,9 +188,6 @@ pub async fn augment_transaction_with_ordinal_transfers( input_index, watched_satpoint.offset, network, - coinbase_tx, - coinbase_subsidy, - cumulated_fees, ctx, ); @@ -253,7 +214,6 @@ pub async fn augment_transaction_with_ordinal_transfers( .push(OrdinalOperation::InscriptionTransferred(transfer_data)); } } - *cumulated_fees += tx.metadata.fee; Ok(transfers) } @@ -275,21 +235,10 @@ mod test { .add_input(TestTxInBuilder::new().value(10_000).build()) .add_output(TestTxOutBuilder::new().value(2_000).build()) .build(); - let coinbase_tx = &TestTransactionBuilder::new() - .add_output(TestTxOutBuilder::new().value(312_500_000 + 5_000).build()) - .build(); - let (destination, satpoint, value) = compute_satpoint_post_transfer( - tx, - 0, - // This offset will make it go to fees. - 5_000, - &Network::Bitcoin, - coinbase_tx, - 312_500_000, - &mut 0, - &ctx, - ); + // This 5000 offset will make it go to fees. + let (destination, satpoint, value) = + compute_satpoint_post_transfer(tx, 0, 5_000, &Network::Bitcoin, &ctx); assert_eq!( destination, @@ -297,8 +246,7 @@ mod test { ); assert_eq!( satpoint, - "b61b0172d95e266c18aea0c624db987e971a5d6d4ebc2aaed85da4642d635735:0:312503000" - .to_string() + "0000000000000000000000000000000000000000000000000000000000000000:0:0" ); assert_eq!(value, None); } @@ -316,20 +264,9 @@ mod test { .build() ) .build(); - let coinbase_tx = &TestTransactionBuilder::new() - .add_output(TestTxOutBuilder::new().value(312_500_000).build()) - .build(); - let (destination, satpoint, value) = compute_satpoint_post_transfer( - tx, - 0, - 5_000, - &Network::Bitcoin, - coinbase_tx, - 312_500_000, - &mut 0, - &ctx, - ); + let (destination, satpoint, value) = + compute_satpoint_post_transfer(tx, 0, 5_000, &Network::Bitcoin, &ctx); assert_eq!( destination, diff --git a/components/ordhook-core/src/core/protocol/sequence_cursor.rs b/components/ordhook-core/src/core/protocol/sequence_cursor.rs index a02bd28..b807964 100644 --- a/components/ordhook-core/src/core/protocol/sequence_cursor.rs +++ b/components/ordhook-core/src/core/protocol/sequence_cursor.rs @@ -16,6 +16,7 @@ pub struct SequenceCursor { pos_cursor: Option, neg_cursor: Option, jubilee_cursor: Option, + unbound_cursor: Option, current_block_height: u64, } @@ -25,6 +26,7 @@ impl SequenceCursor { jubilee_cursor: None, pos_cursor: None, neg_cursor: None, + unbound_cursor: None, current_block_height: 0, } } @@ -33,6 +35,7 @@ impl SequenceCursor { self.pos_cursor = None; self.neg_cursor = None; self.jubilee_cursor = None; + self.unbound_cursor = None; self.current_block_height = 0; } @@ -76,6 +79,12 @@ impl SequenceCursor { Ok(()) } + pub async fn increment_unbound(&mut self, client: &T) -> Result { + let next = self.pick_next_unbound(client).await?; + self.unbound_cursor = Some(next); + Ok(next) + } + async fn pick_next_pos_classic(&mut self, client: &T) -> Result { match self.pos_cursor { None => { @@ -122,6 +131,19 @@ impl SequenceCursor { } } + async fn pick_next_unbound(&mut self, client: &T) -> Result { + match self.unbound_cursor { + None => match ordinals_pg::get_highest_unbound_inscription_sequence(client).await? { + Some(unbound_sequence) => { + self.unbound_cursor = Some(unbound_sequence); + Ok(unbound_sequence + 1) + } + _ => Ok(0), + }, + Some(value) => Ok(value + 1), + } + } + async fn increment_neg_classic(&mut self, client: &T) -> Result<(), String> { self.neg_cursor = Some(self.pick_next_neg_classic(client).await?); Ok(()) @@ -146,6 +168,7 @@ mod test { use bitcoin::Network; use chainhook_postgres::{pg_begin, pg_pool_client}; + use chainhook_types::OrdinalOperation; use test_case::test_case; use crate::{ @@ -204,4 +227,30 @@ mod test { pg_reset_db(&mut pg_client).await?; Ok(result) } + + #[test_case(None => Ok(0); "without sequence")] + #[test_case(Some(21) => Ok(22); "with current sequence")] + #[tokio::test] + async fn picks_next_unbound_sequence(curr_sequence: Option) -> Result { + let mut pg_client = pg_test_connection().await; + ordinals_pg::migrate(&mut pg_client).await?; + let result = { + let mut ord_client = pg_pool_client(&pg_test_connection_pool()).await?; + let client = pg_begin(&mut ord_client).await?; + + let mut tx = TestTransactionBuilder::new_with_operation().build(); + if let OrdinalOperation::InscriptionRevealed(data) = + &mut tx.metadata.ordinal_operations[0] + { + data.unbound_sequence = curr_sequence; + }; + let block = TestBlockBuilder::new().transactions(vec![tx]).build(); + insert_block(&block, &client).await?; + + let mut cursor = SequenceCursor::new(); + cursor.increment_unbound(&client).await? + }; + pg_reset_db(&mut pg_client).await?; + Ok(result) + } } diff --git a/components/ordhook-core/src/core/test_builders.rs b/components/ordhook-core/src/core/test_builders.rs index 24118d1..ac6832c 100644 --- a/components/ordhook-core/src/core/test_builders.rs +++ b/components/ordhook-core/src/core/test_builders.rs @@ -105,6 +105,7 @@ impl TestTransactionBuilder { satpoint_post_inscription: "b61b0172d95e266c18aea0c624db987e971a5d6d4ebc2aaed85da4642d635735:0:0".to_string(), curse_type: None, charms: 0, + unbound_sequence: None, }, )]; tx diff --git a/components/ordhook-core/src/db/models/db_inscription.rs b/components/ordhook-core/src/db/models/db_inscription.rs index cbc0ecd..7aedd00 100644 --- a/components/ordhook-core/src/db/models/db_inscription.rs +++ b/components/ordhook-core/src/db/models/db_inscription.rs @@ -33,6 +33,7 @@ pub struct DbInscription { pub delegate: Option, pub timestamp: PgBigIntU32, pub charms: PgBigIntU32, + pub unbound_sequence: Option, } impl DbInscription { @@ -84,6 +85,7 @@ impl DbInscription { delegate: reveal.delegate.clone(), timestamp: PgBigIntU32(timestamp), charms: PgBigIntU32(reveal.charms as u32), + unbound_sequence: reveal.unbound_sequence, } } } @@ -114,6 +116,7 @@ impl FromPgRow for DbInscription { delegate: row.get("delegate"), timestamp: row.get("timestamp"), charms: row.get("charms"), + unbound_sequence: row.get("unbound_sequence"), } } } diff --git a/components/ordhook-core/src/db/models/db_inscription_recursion.rs b/components/ordhook-core/src/db/models/db_inscription_recursion.rs index 445cf6d..c15a035 100644 --- a/components/ordhook-core/src/db/models/db_inscription_recursion.rs +++ b/components/ordhook-core/src/db/models/db_inscription_recursion.rs @@ -62,6 +62,7 @@ mod test { satpoint_post_inscription: "e47a70a218dfa746ba410b1c057403bb481523d830562fd8dec61ec4d2915e5f:0:0".to_string(), curse_type: None, charms: 0, + unbound_sequence: None, }; let recursions = DbInscriptionRecursion::from_reveal(&reveal).unwrap(); assert_eq!(2, recursions.len()); diff --git a/components/ordhook-core/src/db/ordinals_pg.rs b/components/ordhook-core/src/db/ordinals_pg.rs index 0660c30..bbb66a2 100644 --- a/components/ordhook-core/src/db/ordinals_pg.rs +++ b/components/ordhook-core/src/db/ordinals_pg.rs @@ -98,6 +98,20 @@ pub async fn get_lowest_cursed_classic_inscription_number( Ok(min) } +pub async fn get_highest_unbound_inscription_sequence( + client: &T, +) -> Result, String> { + let row = client + .query_opt("SELECT MAX(unbound_sequence) AS max FROM inscriptions", &[]) + .await + .map_err(|e| format!("get_highest_unbound_inscription_sequence: {e}"))?; + let Some(row) = row else { + return Ok(None); + }; + let max: Option = row.get("max"); + Ok(max) +} + pub async fn get_reinscriptions_for_block( inscriptions_data: &mut BTreeMap<(TransactionIdentifier, usize, u64), TraversalResult>, client: &T, @@ -261,15 +275,16 @@ async fn insert_inscriptions( params.push(&row.delegate); params.push(&row.timestamp); params.push(&row.charms); + params.push(&row.unbound_sequence); } client .query( &format!("INSERT INTO inscriptions (inscription_id, ordinal_number, number, classic_number, block_height, block_hash, tx_id, tx_index, address, mime_type, content_type, content_length, content, fee, curse_type, recursive, input_index, pointer, metadata, - metaprotocol, delegate, timestamp, charms) + metaprotocol, delegate, timestamp, charms, unbound_sequence) VALUES {} - ON CONFLICT (number) DO NOTHING", utils::multi_row_query_param_str(chunk.len(), 23)), + ON CONFLICT (number) DO NOTHING", utils::multi_row_query_param_str(chunk.len(), 24)), ¶ms, ) .await @@ -1206,6 +1221,7 @@ mod test { satpoint_post_inscription: "b61b0172d95e266c18aea0c624db987e971a5d6d4ebc2aaed85da4642d635735:0:0".to_string(), curse_type: None, charms: 0, + unbound_sequence: None, }, )) .build() diff --git a/migrations/ordinals/V17__unbound_inscription_sequence.sql b/migrations/ordinals/V17__unbound_inscription_sequence.sql new file mode 100644 index 0000000..6df0b1b --- /dev/null +++ b/migrations/ordinals/V17__unbound_inscription_sequence.sql @@ -0,0 +1 @@ +ALTER TABLE inscriptions ADD COLUMN unbound_sequence BIGINT UNIQUE;