From 7a8dca53c116fb76e1e7d026c7cdf6863cd74ccd Mon Sep 17 00:00:00 2001 From: Ludo Galabru Date: Fri, 24 May 2024 11:36:47 -0600 Subject: [PATCH] fix: issue #296 (#300) * fix: synthetic predicate * fix: set height 0 for regtest, etc. * fix: issue #296 * chore: update chainhook-sdk * fix: use latest chainhook * fix: augment block with brc20 while indexing * fix: force input 0 --------- Co-authored-by: Rafael Cardenas --- Cargo.lock | 36 +-- components/ordhook-cli/src/cli/mod.rs | 15 +- components/ordhook-core/Cargo.toml | 6 +- .../src/core/meta_protocols/brc20/mod.rs | 12 +- components/ordhook-core/src/core/mod.rs | 19 +- .../processors/inscription_indexing.rs | 2 +- .../core/protocol/inscription_sequencing.rs | 20 +- .../src/core/protocol/satoshi_tracking.rs | 42 +++- components/ordhook-core/src/scan/bitcoin.rs | 14 +- components/ordhook-core/src/service/mod.rs | 207 ++++++++++++------ 10 files changed, 235 insertions(+), 138 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 222fdc7..3af0f8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,9 +494,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chainhook-sdk" -version = "0.12.8" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd54af8c8b55ce530013e2dd510d76be08e9132c9918f81d8a0d3d86b6e4bde" +checksum = "14eeb8a1a055202ca86122c9508dc4f9d9cd8643ef9f25773173b645c33b09ac" dependencies = [ "base58", "base64 0.21.5", @@ -517,7 +517,7 @@ dependencies = [ "regex", "reqwest", "rocket", - "schemars 0.8.16", + "schemars 0.8.16 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde-hex", "serde_derive", @@ -535,7 +535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63f0d358e5c530dd6888e4678ef4ba3f7782525653a1012d33e96a48020c418d" dependencies = [ "hex", - "schemars 0.8.16", + "schemars 0.8.16 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_derive", "serde_json", @@ -2472,7 +2472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64853d7ab065474e87696f7601cee817d200e86c42e04004e005cb3e20c3c5" dependencies = [ "log", - "schemars 0.8.16", + "schemars 0.8.16 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_json", ] @@ -2520,7 +2520,7 @@ dependencies = [ "rocket_okapi", "rocksdb", "rusqlite", - "schemars 0.8.12", + "schemars 0.8.16 (git+https://github.com/hirosystems/schemars.git?branch=feat-chainhook-fixes)", "serde", "serde_derive", "serde_json", @@ -3237,7 +3237,7 @@ dependencies = [ "okapi", "rocket", "rocket_okapi_codegen", - "schemars 0.8.16", + "schemars 0.8.16 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_json", ] @@ -3387,11 +3387,13 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.12" -source = "git+https://github.com/hirosystems/schemars.git?branch=feat-chainhook-fixes#15fdd4711700114d57c090aad62516593bd4ca6d" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "dyn-clone", - "schemars_derive 0.8.12", + "indexmap 1.9.3", + "schemars_derive 0.8.16 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_json", ] @@ -3399,20 +3401,19 @@ dependencies = [ [[package]] name = "schemars" version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +source = "git+https://github.com/hirosystems/schemars.git?branch=feat-chainhook-fixes#d0c10b50478a06198a54e5b90be460112b38b357" dependencies = [ "dyn-clone", - "indexmap 1.9.3", - "schemars_derive 0.8.16", + "schemars_derive 0.8.16 (git+https://github.com/hirosystems/schemars.git?branch=feat-chainhook-fixes)", "serde", "serde_json", ] [[package]] name = "schemars_derive" -version = "0.8.12" -source = "git+https://github.com/hirosystems/schemars.git?branch=feat-chainhook-fixes#15fdd4711700114d57c090aad62516593bd4ca6d" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ "proc-macro2", "quote", @@ -3423,8 +3424,7 @@ dependencies = [ [[package]] name = "schemars_derive" version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +source = "git+https://github.com/hirosystems/schemars.git?branch=feat-chainhook-fixes#d0c10b50478a06198a54e5b90be460112b38b357" dependencies = [ "proc-macro2", "quote", diff --git a/components/ordhook-cli/src/cli/mod.rs b/components/ordhook-cli/src/cli/mod.rs index 111f8e3..16fe5ce 100644 --- a/components/ordhook-cli/src/cli/mod.rs +++ b/components/ordhook-cli/src/cli/mod.rs @@ -4,7 +4,7 @@ use clap::{Parser, Subcommand}; use hiro_system_kit; use ordhook::chainhook_sdk::bitcoincore_rpc::{Auth, Client, RpcApi}; use ordhook::chainhook_sdk::chainhooks::types::{ - BitcoinChainhookSpecification, HttpHook, InscriptionFeedData, + BitcoinChainhookSpecification, HttpHook, InscriptionFeedData, OrdinalsMetaProtocol, }; use ordhook::chainhook_sdk::chainhooks::types::{ BitcoinPredicateType, ChainhookFullSpecification, HookAction, OrdinalOperations, @@ -31,11 +31,12 @@ use ordhook::db::{ open_readonly_ordhook_db_conn_rocks_db, BlockBytesCursor, }; use ordhook::download::download_ordinals_dataset_if_required; -use ordhook::{hex, initialize_db}; use ordhook::scan::bitcoin::scan_bitcoin_chainstate_via_rpc_using_predicate; use ordhook::service::observers::initialize_observers_db; use ordhook::service::{start_observer_forwarding, Service}; +use ordhook::{hex, initialize_db}; use reqwest::Client as HttpClient; +use std::collections::HashSet; use std::io::{BufReader, Read}; use std::path::PathBuf; use std::process; @@ -972,6 +973,12 @@ pub fn build_predicate_from_cli( (Some(start), None) => (Some(start), None, None), _ => unreachable!(), }; + let mut meta_protocols: Option> = None; + if config.meta_protocols.brc20 { + let mut meta = HashSet::::new(); + meta.insert(OrdinalsMetaProtocol::All); + meta_protocols = Some(meta.clone()); + } let predicate = BitcoinChainhookSpecification { network: config.network.bitcoin_network.clone(), uuid: post_to.to_string(), @@ -989,9 +996,7 @@ pub fn build_predicate_from_cli( expired_at: None, enabled: true, predicate: BitcoinPredicateType::OrdinalsProtocol(OrdinalOperations::InscriptionFeed( - InscriptionFeedData { - meta_protocols: None, - }, + InscriptionFeedData { meta_protocols }, )), action: HookAction::HttpPost(HttpHook { url: post_to.to_string(), diff --git a/components/ordhook-core/Cargo.toml b/components/ordhook-core/Cargo.toml index c3ea4aa..f345436 100644 --- a/components/ordhook-core/Cargo.toml +++ b/components/ordhook-core/Cargo.toml @@ -11,8 +11,8 @@ serde_derive = "1" hex = "0.4.3" rand = "0.8.5" lru = "0.12.3" -chainhook-sdk = { version = "=0.12.8", features = ["zeromq"] } -# chainhook-sdk = { version = "=0.12.5", path = "../../../chainhook/components/chainhook-sdk", features = ["zeromq"] } +chainhook-sdk = { version = "=0.12.10", features = ["zeromq"] } +# chainhook-sdk = { version = "=0.12.10", path = "../../../chainhook/components/chainhook-sdk", features = ["zeromq"] } hiro-system-kit = "0.3.1" reqwest = { version = "0.11", default-features = false, features = [ "stream", @@ -35,7 +35,7 @@ dashmap = "5.4.0" fxhash = "0.2.1" rusqlite = { version = "0.28.0", features = ["bundled"] } anyhow = { version = "1.0.56", features = ["backtrace"] } -schemars = { version = "0.8.10", git = "https://github.com/hirosystems/schemars.git", branch = "feat-chainhook-fixes" } +schemars = { version = "0.8.16", git = "https://github.com/hirosystems/schemars.git", branch = "feat-chainhook-fixes" } progressing = '3' futures = "0.3.28" rocksdb = { version = "0.21.0", default-features = false, features = [ diff --git a/components/ordhook-core/src/core/meta_protocols/brc20/mod.rs b/components/ordhook-core/src/core/meta_protocols/brc20/mod.rs index 5641a7c..cbda9eb 100644 --- a/components/ordhook-core/src/core/meta_protocols/brc20/mod.rs +++ b/components/ordhook-core/src/core/meta_protocols/brc20/mod.rs @@ -9,17 +9,17 @@ pub mod test_utils; pub fn brc20_activation_height(network: &BitcoinNetwork) -> u64 { match network { BitcoinNetwork::Mainnet => 779832, - BitcoinNetwork::Regtest => todo!(), - BitcoinNetwork::Testnet => todo!(), - BitcoinNetwork::Signet => todo!(), + BitcoinNetwork::Regtest => 0, + BitcoinNetwork::Testnet => 0, + BitcoinNetwork::Signet => 0, } } pub fn brc20_self_mint_activation_height(network: &BitcoinNetwork) -> u64 { match network { BitcoinNetwork::Mainnet => 837090, - BitcoinNetwork::Regtest => todo!(), - BitcoinNetwork::Testnet => todo!(), - BitcoinNetwork::Signet => todo!(), + BitcoinNetwork::Regtest => 0, + BitcoinNetwork::Testnet => 0, + BitcoinNetwork::Signet => 0, } } diff --git a/components/ordhook-core/src/core/mod.rs b/components/ordhook-core/src/core/mod.rs index f62f84c..00e8107 100644 --- a/components/ordhook-core/src/core/mod.rs +++ b/components/ordhook-core/src/core/mod.rs @@ -79,7 +79,6 @@ pub fn resolve_absolute_pointer(inputs: &Vec, absolute_pointer_value: u64) } pub fn compute_next_satpoint_data( - _tx_index: usize, input_index: usize, inputs: &Vec, outputs: &Vec, @@ -220,40 +219,39 @@ pub fn should_sync_ordhook_db( #[test] fn test_identify_next_output_index_destination() { assert_eq!( - compute_next_satpoint_data(0, 0, &vec![20, 30, 45], &vec![20, 30, 45], 10, None), + compute_next_satpoint_data(0, &vec![20, 30, 45], &vec![20, 30, 45], 10, None), SatPosition::Output((0, 10)) ); assert_eq!( - compute_next_satpoint_data(0, 0, &vec![20, 30, 45], &vec![20, 30, 45], 20, None), + compute_next_satpoint_data(0, &vec![20, 30, 45], &vec![20, 30, 45], 20, None), SatPosition::Output((1, 0)) ); assert_eq!( - compute_next_satpoint_data(0, 1, &vec![20, 30, 45], &vec![20, 30, 45], 25, None), + compute_next_satpoint_data(1, &vec![20, 30, 45], &vec![20, 30, 45], 25, None), SatPosition::Output((1, 25)) ); assert_eq!( - compute_next_satpoint_data(0, 1, &vec![20, 30, 45], &vec![20, 5, 45], 26, None), + compute_next_satpoint_data(1, &vec![20, 30, 45], &vec![20, 5, 45], 26, None), SatPosition::Output((2, 21)) ); assert_eq!( - compute_next_satpoint_data(0, 1, &vec![10, 10, 10], &vec![30], 20, None), + compute_next_satpoint_data(1, &vec![10, 10, 10], &vec![30], 20, None), SatPosition::Fee(0) ); assert_eq!( - compute_next_satpoint_data(0, 0, &vec![10, 10, 10], &vec![30], 30, None), + compute_next_satpoint_data(0, &vec![10, 10, 10], &vec![30], 30, None), SatPosition::Fee(0) ); assert_eq!( - compute_next_satpoint_data(0, 0, &vec![10, 10, 10], &vec![30], 0, None), + compute_next_satpoint_data(0, &vec![10, 10, 10], &vec![30], 0, None), SatPosition::Output((0, 0)) ); assert_eq!( - compute_next_satpoint_data(0, 2, &vec![20, 30, 45], &vec![20, 30, 45], 95, None), + compute_next_satpoint_data(2, &vec![20, 30, 45], &vec![20, 30, 45], 95, None), SatPosition::Fee(50) ); assert_eq!( compute_next_satpoint_data( - 0, 2, &vec![1000, 600, 546, 63034], &vec![1600, 10000, 15000], @@ -264,7 +262,6 @@ fn test_identify_next_output_index_destination() { ); assert_eq!( compute_next_satpoint_data( - 0, 3, &vec![6100, 148660, 103143, 7600], &vec![81434, 173995], diff --git a/components/ordhook-core/src/core/pipeline/processors/inscription_indexing.rs b/components/ordhook-core/src/core/pipeline/processors/inscription_indexing.rs index aaa0cfa..29d408f 100644 --- a/components/ordhook-core/src/core/pipeline/processors/inscription_indexing.rs +++ b/components/ordhook-core/src/core/pipeline/processors/inscription_indexing.rs @@ -333,7 +333,7 @@ pub fn process_block( if let Some(brc20_db_tx) = brc20_db_tx { write_brc20_block_operations( - &block, + block, &mut brc20_operation_map, brc20_cache, &brc20_db_tx, diff --git a/components/ordhook-core/src/core/protocol/inscription_sequencing.rs b/components/ordhook-core/src/core/protocol/inscription_sequencing.rs index aa15374..c77dd9b 100644 --- a/components/ordhook-core/src/core/protocol/inscription_sequencing.rs +++ b/components/ordhook-core/src/core/protocol/inscription_sequencing.rs @@ -617,7 +617,7 @@ pub fn augment_block_with_ordinals_inscriptions_data( let network = get_bitcoin_network(&block.metadata.network); let coinbase_subsidy = Height(block.block_identifier.index).subsidy(); - let coinbase_txid = &block.transactions[0].transaction_identifier.clone(); + let coinbase_tx = &block.transactions[0].clone(); let mut cumulated_fees = 0u64; for (tx_index, tx) in block.transactions.iter_mut().enumerate() { @@ -628,7 +628,7 @@ pub fn augment_block_with_ordinals_inscriptions_data( sequence_cursor, &network, inscriptions_data, - coinbase_txid, + coinbase_tx, coinbase_subsidy, &mut cumulated_fees, &mut sats_overflows, @@ -684,7 +684,7 @@ fn augment_transaction_with_ordinals_inscriptions_data( sequence_cursor: &mut SequenceCursor, network: &Network, inscriptions_data: &mut BTreeMap<(TransactionIdentifier, usize, u64), TraversalResult>, - coinbase_txid: &TransactionIdentifier, + coinbase_tx: &BitcoinTransactionData, coinbase_subsidy: u64, cumulated_fees: &mut u64, sats_overflows: &mut VecDeque<(usize, usize)>, @@ -773,11 +773,10 @@ fn augment_transaction_with_ordinals_inscriptions_data( let (destination, satpoint_post_transfer, output_value) = compute_satpoint_post_transfer( &&*tx, - tx_index, input_index, relative_offset, network, - coinbase_txid, + coinbase_tx, coinbase_subsidy, cumulated_fees, ctx, @@ -842,7 +841,7 @@ fn augment_transaction_with_ordinals_inscriptions_data( fn consolidate_transaction_with_pre_computed_inscription_data( tx: &mut BitcoinTransactionData, tx_index: usize, - coinbase_txid: &TransactionIdentifier, + coinbase_tx: &BitcoinTransactionData, coinbase_subsidy: u64, cumulated_fees: &mut u64, network: &Network, @@ -889,11 +888,10 @@ fn consolidate_transaction_with_pre_computed_inscription_data( // Compute satpoint_post_inscription let (destination, satpoint_post_transfer, output_value) = compute_satpoint_post_transfer( tx, - tx_index, input_index, relative_offset, network, - coinbase_txid, + coinbase_tx, coinbase_subsidy, cumulated_fees, ctx, @@ -930,7 +928,7 @@ pub fn consolidate_block_with_pre_computed_ordinals_data( ) { let network = get_bitcoin_network(&block.metadata.network); let coinbase_subsidy = Height(block.block_identifier.index).subsidy(); - let coinbase_txid = &block.transactions[0].transaction_identifier.clone(); + let coinbase_tx = &block.transactions[0].clone(); let mut cumulated_fees = 0; let expected_inscriptions_count = get_inscriptions_revealed_in_block(&block).len(); let mut inscriptions_data = loop { @@ -959,7 +957,7 @@ pub fn consolidate_block_with_pre_computed_ordinals_data( consolidate_transaction_with_pre_computed_inscription_data( tx, tx_index, - coinbase_txid, + &coinbase_tx, coinbase_subsidy, &mut cumulated_fees, &network, @@ -973,7 +971,7 @@ pub fn consolidate_block_with_pre_computed_ordinals_data( tx, tx_index, &network, - &coinbase_txid, + &coinbase_tx, coinbase_subsidy, &mut cumulated_fees, inscriptions_db_tx, diff --git a/components/ordhook-core/src/core/protocol/satoshi_tracking.rs b/components/ordhook-core/src/core/protocol/satoshi_tracking.rs index 638920c..99b1a92 100644 --- a/components/ordhook-core/src/core/protocol/satoshi_tracking.rs +++ b/components/ordhook-core/src/core/protocol/satoshi_tracking.rs @@ -4,7 +4,7 @@ use chainhook_sdk::{ bitcoincore_rpc_json::bitcoin::{Address, Network, ScriptBuf}, types::{ BitcoinBlockData, BitcoinTransactionData, OrdinalInscriptionTransferData, - OrdinalInscriptionTransferDestination, OrdinalOperation, TransactionIdentifier, + OrdinalInscriptionTransferDestination, OrdinalOperation, }, utils::Context, }; @@ -31,14 +31,14 @@ pub fn augment_block_with_ordinals_transfer_data( let network = get_bitcoin_network(&block.metadata.network); let coinbase_subsidy = Height(block.block_identifier.index).subsidy(); - let coinbase_txid = &block.transactions[0].transaction_identifier.clone(); + let coinbase_tx = &block.transactions[0].clone(); let mut cumulated_fees = 0; for (tx_index, tx) in block.transactions.iter_mut().enumerate() { let transfers = augment_transaction_with_ordinals_transfers_data( tx, tx_index, &network, - &coinbase_txid, + &coinbase_tx, coinbase_subsidy, &mut cumulated_fees, inscriptions_db_tx, @@ -73,11 +73,10 @@ pub fn augment_block_with_ordinals_transfer_data( pub fn compute_satpoint_post_transfer( tx: &BitcoinTransactionData, - tx_index: usize, input_index: usize, relative_pointer_value: u64, network: &Network, - coinbase_txid: &TransactionIdentifier, + coinbase_tx: &BitcoinTransactionData, coinbase_subsidy: u64, cumulated_fees: &mut u64, ctx: &Context, @@ -90,7 +89,6 @@ pub fn compute_satpoint_post_transfer( .collect::<_>(); let outputs = tx.metadata.outputs.iter().map(|o| o.value).collect::<_>(); let post_transfer_data = compute_next_satpoint_data( - tx_index, input_index, &inputs, &outputs, @@ -141,10 +139,33 @@ pub fn compute_satpoint_post_transfer( SatPosition::Fee(offset) => { // Get Coinbase TX let total_offset = coinbase_subsidy + *cumulated_fees + offset; - let outpoint = format_outpoint_to_watch(&coinbase_txid, 0); + 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, + _ => { + ctx.try_log(|logger| { + info!( + logger, + "unable to locate satoshi in coinbase outputs", + ) + }); + (0, total_offset) + } + }; + + let outpoint = format_outpoint_to_watch(&coinbase_tx.transaction_identifier, output_index); ( outpoint, - total_offset, + offset, OrdinalInscriptionTransferDestination::SpentInFees, None, ) @@ -163,7 +184,7 @@ pub fn augment_transaction_with_ordinals_transfers_data( tx: &mut BitcoinTransactionData, tx_index: usize, network: &Network, - coinbase_txid: &TransactionIdentifier, + coinbase_tx: &BitcoinTransactionData, coinbase_subsidy: u64, cumulated_fees: &mut u64, inscriptions_db_tx: &Transaction, @@ -203,11 +224,10 @@ pub fn augment_transaction_with_ordinals_transfers_data( let (destination, satpoint_post_transfer, post_transfer_output_value) = compute_satpoint_post_transfer( &&*tx, - tx_index, input_index, watched_satpoint.offset, network, - coinbase_txid, + coinbase_tx, coinbase_subsidy, cumulated_fees, ctx, diff --git a/components/ordhook-core/src/scan/bitcoin.rs b/components/ordhook-core/src/scan/bitcoin.rs index c9a939d..1b52258 100644 --- a/components/ordhook-core/src/scan/bitcoin.rs +++ b/components/ordhook-core/src/scan/bitcoin.rs @@ -105,14 +105,10 @@ pub async fn scan_bitcoin_chainstate_via_rpc_using_predicate( BitcoinPredicateType::OrdinalsProtocol(OrdinalOperations::InscriptionFeed( ref feed_data, )) if feed_data.meta_protocols.is_some() => { - if current_block_height >= brc20_activation_height(&bitcoin_config.network) { - Some(open_readonly_brc20_db_conn( - &config.expected_cache_path(), - ctx, - )?) - } else { - None - } + Some(open_readonly_brc20_db_conn( + &config.expected_cache_path(), + ctx, + )?) } _ => None, }; @@ -156,7 +152,7 @@ pub async fn scan_bitcoin_chainstate_via_rpc_using_predicate( &inscriptions_db_tx, true, brc20_db_conn.as_ref(), - &Context::empty(), + &ctx, ); } diff --git a/components/ordhook-core/src/service/mod.rs b/components/ordhook-core/src/service/mod.rs index ea0d5c1..e63a4bd 100644 --- a/components/ordhook-core/src/service/mod.rs +++ b/components/ordhook-core/src/service/mod.rs @@ -3,13 +3,13 @@ pub mod observers; mod runloops; use crate::config::{Config, PredicatesApi}; +use crate::core::meta_protocols::brc20::brc20_activation_height; use crate::core::meta_protocols::brc20::cache::Brc20MemoryCache; use crate::core::meta_protocols::brc20::db::open_readwrite_brc20_db_conn; use crate::core::meta_protocols::brc20::parser::ParsedBrc20Operation; use crate::core::meta_protocols::brc20::verifier::{ verify_brc20_operation, verify_brc20_transfer, VerifiedBrc20Operation, }; -use crate::core::meta_protocols::brc20::brc20_activation_height; use crate::core::pipeline::download_and_pipeline_blocks; use crate::core::pipeline::processors::block_archiving::start_block_archiving_processor; use crate::core::pipeline::processors::inscription_indexing::process_block; @@ -47,7 +47,10 @@ use chainhook_sdk::observer::{ start_event_observer, BitcoinBlockDataCached, DataHandlerEvent, EventObserverConfig, HandleBlock, ObserverCommand, ObserverEvent, ObserverSidecar, }; -use chainhook_sdk::types::{BitcoinBlockData, BlockIdentifier, OrdinalOperation}; +use chainhook_sdk::types::{ + BitcoinBlockData, BlockIdentifier, Brc20BalanceData, Brc20Operation, Brc20TokenDeployData, + Brc20TransferData, OrdinalOperation, +}; use chainhook_sdk::utils::{BlockHeights, Context}; use crossbeam_channel::unbounded; use crossbeam_channel::{select, Sender}; @@ -857,7 +860,7 @@ pub fn chainhook_sidecar_mutate_blocks( } pub fn write_brc20_block_operations( - block: &BitcoinBlockData, + block: &mut BitcoinBlockData, brc20_operation_map: &mut HashMap, brc20_cache: &mut Brc20MemoryCache, db_tx: &Transaction, @@ -866,7 +869,7 @@ pub fn write_brc20_block_operations( if block.block_identifier.index < brc20_activation_height(&block.metadata.network) { return; } - for (tx_index, tx) in block.transactions.iter().enumerate() { + for (tx_index, tx) in block.transactions.iter_mut().enumerate() { for op in tx.metadata.ordinal_operations.iter() { match op { OrdinalOperation::InscriptionRevealed(reveal) => { @@ -882,68 +885,121 @@ pub fn write_brc20_block_operations( &db_tx, &ctx, ) { - Ok(op) => { - match op { - VerifiedBrc20Operation::TokenDeploy(token) => { - brc20_cache.insert_token_deploy( - &token, - reveal, - &block.block_identifier, - tx_index as u64, - db_tx, - ctx, - ); - ctx.try_log(|logger| { - info!( - logger, - "BRC-20 deploy {} ({}) at block {}", - token.tick, - token.address, - block.block_identifier.index - ) - }); - } - VerifiedBrc20Operation::TokenMint(balance) => { - brc20_cache.insert_token_mint( - &balance, - reveal, - &block.block_identifier, - tx_index as u64, - db_tx, - ctx, - ); - ctx.try_log(|logger| { - info!( + Ok(op) => match op { + VerifiedBrc20Operation::TokenDeploy(token) => { + let dec = token.dec as usize; + tx.metadata.brc20_operation = + Some(Brc20Operation::Deploy(Brc20TokenDeployData { + tick: token.tick.clone(), + max: format!( + "{:.precision$}", + token.max, + precision = dec + ), + lim: format!( + "{:.precision$}", + token.lim, + precision = dec + ), + dec: token.dec.to_string(), + address: token.address.clone(), + inscription_id: reveal.inscription_id.clone(), + self_mint: token.self_mint, + })); + brc20_cache.insert_token_deploy( + &token, + reveal, + &block.block_identifier, + tx_index as u64, + db_tx, + ctx, + ); + ctx.try_log(|logger| { + info!( logger, - "BRC-20 mint {} {} ({}) at block {}", - balance.tick, balance.amt, balance.address, + "BRC-20 deploy {} ({}) at block {}", + token.tick, + token.address, block.block_identifier.index ) - }); - } - VerifiedBrc20Operation::TokenTransfer(balance) => { - brc20_cache.insert_token_transfer( - &balance, - reveal, - &block.block_identifier, - tx_index as u64, - db_tx, - ctx, - ); - ctx.try_log(|logger| { - info!( - logger, - "BRC-20 transfer {} {} ({}) at block {}", - balance.tick, balance.amt, balance.address, - block.block_identifier.index - ) - }); - } - VerifiedBrc20Operation::TokenTransferSend(_) => { - unreachable!("BRC-20 token transfer send should never be generated on reveal") - } + }); } - } + VerifiedBrc20Operation::TokenMint(balance) => { + let Some(token) = + brc20_cache.get_token(&balance.tick, db_tx, ctx) + else { + unreachable!(); + }; + tx.metadata.brc20_operation = + Some(Brc20Operation::Mint(Brc20BalanceData { + tick: balance.tick.clone(), + amt: format!( + "{:.precision$}", + balance.amt, + precision = token.dec as usize + ), + address: balance.address.clone(), + inscription_id: reveal.inscription_id.clone(), + })); + brc20_cache.insert_token_mint( + &balance, + reveal, + &block.block_identifier, + tx_index as u64, + db_tx, + ctx, + ); + ctx.try_log(|logger| { + info!( + logger, + "BRC-20 mint {} {} ({}) at block {}", + balance.tick, + balance.amt, + balance.address, + block.block_identifier.index + ) + }); + } + VerifiedBrc20Operation::TokenTransfer(balance) => { + let Some(token) = + brc20_cache.get_token(&balance.tick, db_tx, ctx) + else { + unreachable!(); + }; + tx.metadata.brc20_operation = + Some(Brc20Operation::Transfer(Brc20BalanceData { + tick: balance.tick.clone(), + amt: format!( + "{:.precision$}", + balance.amt, + precision = token.dec as usize + ), + address: balance.address.clone(), + inscription_id: reveal.inscription_id.clone(), + })); + brc20_cache.insert_token_transfer( + &balance, + reveal, + &block.block_identifier, + tx_index as u64, + db_tx, + ctx, + ); + ctx.try_log(|logger| { + info!( + logger, + "BRC-20 transfer {} {} ({}) at block {}", + balance.tick, + balance.amt, + balance.address, + block.block_identifier.index + ) + }); + } + VerifiedBrc20Operation::TokenTransferSend(_) => { + unreachable!("BRC-20 token transfer send should never be generated on reveal") + } + }, Err(e) => { ctx.try_log(|logger| { debug!(logger, "Error validating BRC-20 operation {}", e) @@ -957,6 +1013,28 @@ pub fn write_brc20_block_operations( OrdinalOperation::InscriptionTransferred(transfer) => { match verify_brc20_transfer(transfer, brc20_cache, &db_tx, &ctx) { Ok(data) => { + let Some(token) = brc20_cache.get_token(&data.tick, db_tx, ctx) else { + unreachable!(); + }; + let Some(unsent_transfer) = brc20_cache.get_unsent_token_transfer( + transfer.ordinal_number, + db_tx, + ctx, + ) else { + unreachable!(); + }; + tx.metadata.brc20_operation = + Some(Brc20Operation::TransferSend(Brc20TransferData { + tick: data.tick.clone(), + amt: format!( + "{:.precision$}", + data.amt * -1.0, + precision = token.dec as usize + ), + sender_address: data.sender_address.clone(), + receiver_address: data.receiver_address.clone(), + inscription_id: unsent_transfer.inscription_id, + })); brc20_cache.insert_token_transfer_send( &data, &transfer, @@ -969,7 +1047,10 @@ pub fn write_brc20_block_operations( info!( logger, "BRC-20 transfer_send {} {} ({} -> {}) at block {}", - data.tick, data.amt, data.sender_address, data.receiver_address, + data.tick, + data.amt, + data.sender_address, + data.receiver_address, block.block_identifier.index ) });