diff --git a/components/chainhook-cli/src/cli/mod.rs b/components/chainhook-cli/src/cli/mod.rs index 2ab12681..e79d5d10 100644 --- a/components/chainhook-cli/src/cli/mod.rs +++ b/components/chainhook-cli/src/cli/mod.rs @@ -13,8 +13,8 @@ use chainhook_event_observer::chainhooks::types::{ StacksPrintEventBasedPredicate, }; use chainhook_event_observer::hord::db::{ - fetch_and_cache_blocks_in_hord_db, find_inscriptions_at_wached_outpoint, initialize_hord_db, - open_readonly_hord_db_conn, open_readwrite_hord_db_conn, + fetch_and_cache_blocks_in_hord_db, find_inscriptions_at_wached_outpoint, + find_latest_compacted_block_known, open_readonly_hord_db_conn, open_readwrite_hord_db_conn, retrieve_satoshi_point_using_local_storage, }; use chainhook_event_observer::observer::BitcoinConfig; @@ -203,8 +203,6 @@ struct FindSatPointCommand { pub block_height: u64, /// Txid pub txid: String, - /// Output index - pub output_index: usize, /// Target Devnet network #[clap( long = "devnet", @@ -455,6 +453,21 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { FindCommand::SatPoint(cmd) => { let config = Config::default(cmd.devnet, cmd.testnet, cmd.mainnet, &cmd.config_path)?; + + info!( + ctx.expect_logger(), + "Computing satoshi number for satoshi at offet 0 in 1st output of transaction {} (block ${})", cmd.txid, cmd.block_height + ); + + let hord_db_conn = + open_readonly_hord_db_conn(&config.expected_cache_path(), &ctx).unwrap(); + + let tip_height = find_latest_compacted_block_known(&hord_db_conn) as u64; + + if cmd.block_height > tip_height { + perform_hord_db_update(tip_height, cmd.block_height, 8, &config, &ctx).await?; + } + let transaction_identifier = TransactionIdentifier { hash: cmd.txid.clone(), }; @@ -463,10 +476,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { hash: "".into(), }; - let hord_db_conn = - open_readonly_hord_db_conn(&config.expected_cache_path(), &ctx).unwrap(); - - let (block_height, offset, ordinal_number) = + let (block_height, offset, ordinal_number, hops) = retrieve_satoshi_point_using_local_storage( &hord_db_conn, &block_identifier, @@ -475,7 +485,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { )?; info!( ctx.expect_logger(), - "Block: {block_height}, Offset {offset}:, Ordinal number: {ordinal_number}", + "Satoshi #{ordinal_number} was minted in block #{block_height} at offset {offset} and was transferred {hops} times.", ); } FindCommand::Inscription(cmd) => { @@ -491,14 +501,6 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { Command::Hord(HordCommand::Db(subcmd)) => match subcmd { DbCommand::Init(cmd) => { let config = Config::default(false, false, false, &cmd.config_path)?; - - let bitcoin_config = BitcoinConfig { - username: config.network.bitcoin_node_rpc_username.clone(), - password: config.network.bitcoin_node_rpc_password.clone(), - rpc_url: config.network.bitcoin_node_rpc_url.clone(), - network: config.network.bitcoin_network.clone(), - }; - let auth = Auth::UserPass( config.network.bitcoin_node_rpc_username.clone(), config.network.bitcoin_node_rpc_password.clone(), @@ -521,38 +523,16 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { } }; - let rw_hord_db_conn = initialize_hord_db(&config.expected_cache_path(), &ctx); - - let _ = fetch_and_cache_blocks_in_hord_db( - &bitcoin_config, - &rw_hord_db_conn, - 0, - end_block, - &ctx, - cmd.network_threads, - ) - .await?; + perform_hord_db_update(0, end_block, cmd.network_threads, &config, &ctx).await?; } DbCommand::Update(cmd) => { let config = Config::default(false, false, false, &cmd.config_path)?; - - let bitcoin_config = BitcoinConfig { - username: config.network.bitcoin_node_rpc_username.clone(), - password: config.network.bitcoin_node_rpc_password.clone(), - rpc_url: config.network.bitcoin_node_rpc_url.clone(), - network: config.network.bitcoin_network.clone(), - }; - - let rw_hord_db_conn = - open_readwrite_hord_db_conn(&config.expected_cache_path(), &ctx)?; - - let _ = fetch_and_cache_blocks_in_hord_db( - &bitcoin_config, - &rw_hord_db_conn, + perform_hord_db_update( cmd.start_block, cmd.end_block, - &ctx, cmd.network_threads, + &config, + &ctx, ) .await?; } @@ -561,6 +541,40 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { Ok(()) } +pub async fn perform_hord_db_update( + start_block: u64, + end_block: u64, + network_threads: usize, + config: &Config, + ctx: &Context, +) -> Result<(), String> { + info!( + ctx.expect_logger(), + "Syncing hord_db: {} blocks to download ({start_block}: {end_block}), using {network_threads} network threads", end_block - start_block + ); + + let bitcoin_config = BitcoinConfig { + username: config.network.bitcoin_node_rpc_username.clone(), + password: config.network.bitcoin_node_rpc_password.clone(), + rpc_url: config.network.bitcoin_node_rpc_url.clone(), + network: config.network.bitcoin_network.clone(), + }; + + let rw_hord_db_conn = open_readwrite_hord_db_conn(&config.expected_cache_path(), &ctx)?; + + let _ = fetch_and_cache_blocks_in_hord_db( + &bitcoin_config, + &rw_hord_db_conn, + start_block, + end_block, + &ctx, + network_threads, + ) + .await?; + + Ok(()) +} + #[allow(dead_code)] pub fn install_ctrlc_handler(terminate_tx: Sender, ctx: Context) { ctrlc::set_handler(move || { diff --git a/components/chainhook-event-observer/src/hord/db/mod.rs b/components/chainhook-event-observer/src/hord/db/mod.rs index 2a1dc85f..66ca2c02 100644 --- a/components/chainhook-event-observer/src/hord/db/mod.rs +++ b/components/chainhook-event-observer/src/hord/db/mod.rs @@ -302,7 +302,23 @@ pub fn update_transfered_inscription( } } -pub fn find_last_inscription_number( +pub fn find_latest_inscription_block_height( + hord_db_conn: &Connection, + _ctx: &Context, +) -> Result { + let args: &[&dyn ToSql] = &[]; + let mut stmt = hord_db_conn + .prepare("SELECT block_height FROM inscriptions ORDER BY block_height DESC LIMIT 1") + .unwrap(); + let mut rows = stmt.query(args).unwrap(); + while let Ok(Some(row)) = rows.next() { + let block_height: u64 = row.get(0).unwrap(); + return Ok(block_height); + } + Ok(0) +} + +pub fn find_latest_inscription_number( hord_db_conn: &Connection, _ctx: &Context, ) -> Result { @@ -513,7 +529,7 @@ pub async fn fetch_and_cache_blocks_in_hord_db( let (block_compressed_tx, block_compressed_rx) = crossbeam_channel::unbounded(); let first_inscription_block_height = 767430; - for block_cursor in start_block..end_block { + for block_cursor in start_block..=end_block { let block_height = block_cursor.clone(); let block_hash_tx = block_hash_tx.clone(); let config = bitcoin_config.clone(); @@ -573,7 +589,8 @@ pub async fn fetch_and_cache_blocks_in_hord_db( .expect("unable to spawn thread"); let mut blocks_stored = 0; - let mut cursor = first_inscription_block_height; + let mut cursor = find_latest_inscription_block_height(&rw_hord_db_conn, &ctx) + .unwrap_or(first_inscription_block_height) as usize; let mut inbox = HashMap::new(); while let Ok(Some((block_height, compacted_block, raw_block))) = block_compressed_rx.recv() { @@ -582,7 +599,7 @@ pub async fn fetch_and_cache_blocks_in_hord_db( insert_entry_in_blocks(block_height, &compacted_block, &rw_hord_db_conn, &ctx); // Early return, only considering blocks after 1st inscription - if raw_block.height < first_inscription_block_height { + if raw_block.height < cursor { continue; } let block_height = raw_block.height; @@ -645,7 +662,7 @@ pub fn retrieve_satoshi_point_using_local_storage( block_identifier: &BlockIdentifier, transaction_identifier: &TransactionIdentifier, ctx: &Context, -) -> Result<(u64, u64, u64), String> { +) -> Result<(u64, u64, u64, u32), String> { let mut ordinal_offset = 0; let mut ordinal_block_number = block_identifier.index as u32; let txid = { @@ -653,8 +670,9 @@ pub fn retrieve_satoshi_point_using_local_storage( [bytes[0], bytes[1], bytes[2], bytes[3]] }; let mut tx_cursor = (txid, 0); - + let mut hops: u32 = 0; loop { + hops += 1; let res = match find_compacted_block_at_block_height(ordinal_block_number, &hord_db_conn) { Some(res) => res, None => { @@ -776,5 +794,10 @@ pub fn retrieve_satoshi_point_using_local_storage( let height = Height(ordinal_block_number.into()); let ordinal_number = height.starting_sat().0 + ordinal_offset; - Ok((ordinal_block_number.into(), ordinal_offset, ordinal_number)) + Ok(( + ordinal_block_number.into(), + ordinal_offset, + ordinal_number, + hops, + )) } diff --git a/components/chainhook-event-observer/src/hord/mod.rs b/components/chainhook-event-observer/src/hord/mod.rs index 84839cdc..78069306 100644 --- a/components/chainhook-event-observer/src/hord/mod.rs +++ b/components/chainhook-event-observer/src/hord/mod.rs @@ -13,7 +13,7 @@ use crate::{ hord::{ db::{ find_inscription_with_ordinal_number, find_inscriptions_at_wached_outpoint, - find_last_inscription_number, insert_entry_in_blocks, + find_latest_inscription_number, insert_entry_in_blocks, retrieve_satoshi_point_using_local_storage, store_new_inscription, update_transfered_inscription, CompactedBlock, }, @@ -98,7 +98,7 @@ pub fn update_hord_db_and_augment_bitcoin_block( new_tx.metadata.ordinal_operations.iter_mut().enumerate() { if let OrdinalOperation::InscriptionRevealed(inscription) = ordinal_event { - let (ordinal_block_height, ordinal_offset, ordinal_number) = { + let (ordinal_block_height, ordinal_offset, ordinal_number, _) = { // Are we looking at a re-inscription? let res = retrieve_satoshi_point_using_local_storage( &rw_hord_db_conn, @@ -140,7 +140,7 @@ pub fn update_hord_db_and_augment_bitcoin_block( inscription.ordinal_block_height = ordinal_block_height; inscription.ordinal_number = ordinal_number; inscription.inscription_number = - match find_last_inscription_number(&rw_hord_db_conn, &ctx) { + match find_latest_inscription_number(&rw_hord_db_conn, &ctx) { Ok(inscription_number) => inscription_number, Err(e) => { ctx.try_log(|logger| {