Skip to content

Commit caacff7

Browse files
authored
feat!: support brc20 activity on scan blocks command (#350)
* fix: meta protocols for block scan * chore: show brc20 operations on block scan * docs: readme * chore: generator
1 parent 13e5a97 commit caacff7

File tree

10 files changed

+190
-153
lines changed

10 files changed

+190
-153
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Inscription 6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0 r
4545
Inscription 26482871f33f1051f450f2da9af275794c0b5f1c61ebf35e4467fb42c2813403i0 revealed at block #767753 (ordinal_number 727624168684699, inscription_number 1)
4646
```
4747

48-
In this command, an interval of blocks to scan (starting at block `767430`, ending at block `767753`) is being provided. `ordhook` will display inscriptions and transfers activities occurring in the range of the specified blocks.
48+
In this command, an interval of blocks to scan (starting at block `767430`, ending at block `767753`) is being provided. `ordhook` will display inscriptions and transfers activities occurring in the range of the specified blocks. Add the option `--meta_protocols=brc20` if you wish to explore BRC-20 activity as well.
4949

5050
The activity for a given inscription can be retrieved using the following command:
5151

components/ordhook-cli/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ clap = { version = "3.2.23", features = ["derive"], optional = true }
2424
clap_generate = { version = "3.0.3", optional = true }
2525
toml = { version = "0.5.6", features = ["preserve_order"], optional = true }
2626
ctrlc = { version = "3.2.2", optional = true }
27-
tcmalloc2 = { version = "0.1.2+2.13", optional = true }
27+
tcmalloc2 = { version = "0.1.2", optional = true }
2828

2929
[features]
3030
default = ["cli"]

components/ordhook-cli/src/cli/mod.rs

+66-32
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use ordhook::chainhook_sdk::types::{BitcoinBlockData, TransactionIdentifier};
1616
use ordhook::chainhook_sdk::utils::BlockHeights;
1717
use ordhook::chainhook_sdk::utils::Context;
1818
use ordhook::config::Config;
19-
use ordhook::core::meta_protocols::brc20::db::open_readwrite_brc20_db_conn;
19+
use ordhook::core::meta_protocols::brc20::db::{
20+
get_brc20_operations_on_block, open_readwrite_brc20_db_conn,
21+
};
2022
use ordhook::core::new_traversals_lazy_cache;
2123
use ordhook::core::pipeline::download_and_pipeline_blocks;
2224
use ordhook::core::pipeline::processors::block_archiving::start_block_archiving_processor;
@@ -30,12 +32,12 @@ use ordhook::db::{
3032
open_ordhook_db_conn_rocks_db_loop, open_readonly_ordhook_db_conn,
3133
open_readonly_ordhook_db_conn_rocks_db, open_readwrite_ordhook_dbs, BlockBytesCursor,
3234
};
33-
use ordhook::download::download_ordinals_dataset_if_required;
35+
use ordhook::download::download_archive_datasets_if_required;
3436
use ordhook::scan::bitcoin::scan_bitcoin_chainstate_via_rpc_using_predicate;
3537
use ordhook::service::observers::initialize_observers_db;
3638
use ordhook::service::{start_observer_forwarding, Service};
3739
use ordhook::utils::bitcoind::bitcoind_get_block_height;
38-
use ordhook::{hex, initialize_databases};
40+
use ordhook::{hex, initialize_databases, try_error, try_info, try_warn};
3941
use reqwest::Client as HttpClient;
4042
use std::collections::HashSet;
4143
use std::io::{BufReader, Read};
@@ -118,6 +120,9 @@ struct ScanBlocksCommand {
118120
conflicts_with = "regtest"
119121
)]
120122
pub config_path: Option<String>,
123+
/// Meta protocols
124+
#[clap(long = "meta-protocols", conflicts_with = "config-path")]
125+
pub meta_protocols: Option<String>,
121126
/// HTTP Post activity to a URL
122127
#[clap(long = "post-to")]
123128
pub post_to: Option<String>,
@@ -535,8 +540,13 @@ pub fn main() {
535540
async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
536541
match opts.command {
537542
Command::Scan(ScanCommand::Blocks(cmd)) => {
538-
let config: Config =
539-
ConfigFile::default(cmd.regtest, cmd.testnet, cmd.mainnet, &cmd.config_path)?;
543+
let config: Config = ConfigFile::default(
544+
cmd.regtest,
545+
cmd.testnet,
546+
cmd.mainnet,
547+
&cmd.config_path,
548+
&cmd.meta_protocols,
549+
)?;
540550
// Download dataset if required
541551
// If console:
542552
// - Replay based on SQLite queries
@@ -548,17 +558,14 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
548558
.map_err(|_e| format!("Block start / end block spec invalid"))?;
549559

550560
if let Some(ref post_to) = cmd.post_to {
551-
info!(ctx.expect_logger(), "A fully synchronized bitcoind node is required for retrieving inscriptions content.");
552-
info!(
553-
ctx.expect_logger(),
554-
"Checking {}...", config.network.bitcoind_rpc_url
555-
);
561+
try_info!(ctx, "A fully synchronized bitcoind node is required for retrieving inscriptions content.");
562+
try_info!(ctx, "Checking {}...", config.network.bitcoind_rpc_url);
556563
let tip = bitcoind_get_block_height(&config, ctx);
557564
if let Some(highest_desired) = block_range.pop_back() {
558565
if tip < highest_desired {
559-
error!(ctx.expect_logger(), "Unable to scan desired block range: underlying bitcoind synchronized until block #{} ", tip);
566+
try_error!(ctx, "Unable to scan desired block range: underlying bitcoind synchronized until block #{} ", tip);
560567
} else {
561-
info!(ctx.expect_logger(), "Starting scan");
568+
try_info!(ctx, "Starting scan");
562569
}
563570
block_range.push_back(highest_desired);
564571
}
@@ -582,17 +589,16 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
582589
)
583590
.await?;
584591
} else {
585-
let _ = download_ordinals_dataset_if_required(&config, ctx).await;
592+
download_archive_datasets_if_required(&config, ctx).await;
586593
let mut total_inscriptions = 0;
587594
let mut total_transfers = 0;
588595

589596
let db_connections = initialize_databases(&config, ctx);
590-
let inscriptions_db_conn = db_connections.ordhook;
591597
while let Some(block_height) = block_range.pop_front() {
592598
let inscriptions =
593-
find_all_inscriptions_in_block(&block_height, &inscriptions_db_conn, ctx);
599+
find_all_inscriptions_in_block(&block_height, &db_connections.ordhook, ctx);
594600
let locations =
595-
find_all_transfers_in_block(&block_height, &inscriptions_db_conn, ctx);
601+
find_all_transfers_in_block(&block_height, &db_connections.ordhook, ctx);
596602

597603
let mut total_transfers_in_block = 0;
598604

@@ -618,6 +624,18 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
618624
);
619625
}
620626
}
627+
match db_connections.brc20 {
628+
Some(ref conn) => {
629+
let activity = get_brc20_operations_on_block(block_height, &conn, ctx);
630+
for (_, row) in activity.iter() {
631+
if row.operation == "transfer_receive" {
632+
continue;
633+
}
634+
println!("BRC-20 {} {} {}", row.operation, row.tick, row.avail_balance);
635+
}
636+
}
637+
None => todo!(),
638+
}
621639
if total_transfers_in_block > 0 && !inscriptions.is_empty() {
622640
println!(
623641
"Inscriptions revealed: {}, inscriptions transferred: {total_transfers_in_block}",
@@ -632,15 +650,20 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
632650
if total_transfers == 0 && total_inscriptions == 0 {
633651
let db_file_path =
634652
get_default_ordhook_db_file_path(&config.expected_cache_path());
635-
warn!(ctx.expect_logger(), "No data available. Check the validity of the range being scanned and the validity of your local database {}", db_file_path.display());
653+
try_warn!(ctx, "No data available. Check the validity of the range being scanned and the validity of your local database {}", db_file_path.display());
636654
}
637655
}
638656
}
639657
Command::Scan(ScanCommand::Inscription(cmd)) => {
640-
let config: Config =
641-
ConfigFile::default(cmd.regtest, cmd.testnet, cmd.mainnet, &cmd.config_path)?;
658+
let config: Config = ConfigFile::default(
659+
cmd.regtest,
660+
cmd.testnet,
661+
cmd.mainnet,
662+
&cmd.config_path,
663+
&None,
664+
)?;
642665

643-
let _ = download_ordinals_dataset_if_required(&config, ctx).await;
666+
let _ = download_archive_datasets_if_required(&config, ctx).await;
644667

645668
let inscriptions_db_conn =
646669
open_readonly_ordhook_db_conn(&config.expected_cache_path(), ctx)?;
@@ -663,8 +686,13 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
663686
);
664687
}
665688
Command::Scan(ScanCommand::Transaction(cmd)) => {
666-
let config: Config =
667-
ConfigFile::default(cmd.regtest, cmd.testnet, cmd.mainnet, &cmd.config_path)?;
689+
let config: Config = ConfigFile::default(
690+
cmd.regtest,
691+
cmd.testnet,
692+
cmd.mainnet,
693+
&cmd.config_path,
694+
&None,
695+
)?;
668696
let http_client = build_http_client();
669697
let block = fetch_and_standardize_block(
670698
&http_client,
@@ -702,8 +730,13 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
702730
sleep(Duration::from_secs(3600 * 24 * 7))
703731
}
704732

705-
let config =
706-
ConfigFile::default(cmd.regtest, cmd.testnet, cmd.mainnet, &cmd.config_path)?;
733+
let config = ConfigFile::default(
734+
cmd.regtest,
735+
cmd.testnet,
736+
cmd.mainnet,
737+
&cmd.config_path,
738+
&None,
739+
)?;
707740
let db_connections = initialize_databases(&config, ctx);
708741

709742
let last_known_block =
@@ -769,7 +802,8 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
769802
ConfigCommand::New(cmd) => {
770803
use std::fs::File;
771804
use std::io::Write;
772-
let config = ConfigFile::default(cmd.regtest, cmd.testnet, cmd.mainnet, &None)?;
805+
let config =
806+
ConfigFile::default(cmd.regtest, cmd.testnet, cmd.mainnet, &None, &None)?;
773807
let config_content = generate_config(&config.network.bitcoin_network);
774808
let mut file_path = PathBuf::new();
775809
file_path.push("Ordhook.toml");
@@ -781,7 +815,7 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
781815
}
782816
},
783817
Command::Db(OrdhookDbCommand::New(cmd)) => {
784-
let config = ConfigFile::default(false, false, false, &cmd.config_path)?;
818+
let config = ConfigFile::default(false, false, false, &cmd.config_path, &None)?;
785819
// Create DB
786820
initialize_databases(&config, ctx);
787821
open_ordhook_db_conn_rocks_db_loop(
@@ -793,14 +827,14 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
793827
);
794828
}
795829
Command::Db(OrdhookDbCommand::Sync(cmd)) => {
796-
let config = ConfigFile::default(false, false, false, &cmd.config_path)?;
830+
let config = ConfigFile::default(false, false, false, &cmd.config_path, &None)?;
797831
initialize_databases(&config, ctx);
798832
let service = Service::new(config, ctx.clone());
799833
service.update_state(None).await?;
800834
}
801835
Command::Db(OrdhookDbCommand::Repair(subcmd)) => match subcmd {
802836
RepairCommand::Blocks(cmd) => {
803-
let config = ConfigFile::default(false, false, false, &cmd.config_path)?;
837+
let config = ConfigFile::default(false, false, false, &cmd.config_path, &None)?;
804838
let mut ordhook_config = config.get_ordhook_config();
805839
if let Some(network_threads) = cmd.network_threads {
806840
ordhook_config.resources.bitcoind_rpc_threads = network_threads;
@@ -839,7 +873,7 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
839873
}
840874
}
841875
RepairCommand::Inscriptions(cmd) => {
842-
let config = ConfigFile::default(false, false, false, &cmd.config_path)?;
876+
let config = ConfigFile::default(false, false, false, &cmd.config_path, &None)?;
843877
let mut ordhook_config = config.get_ordhook_config();
844878
if let Some(network_threads) = cmd.network_threads {
845879
ordhook_config.resources.bitcoind_rpc_threads = network_threads;
@@ -867,7 +901,7 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
867901
.await?;
868902
}
869903
RepairCommand::Transfers(cmd) => {
870-
let config = ConfigFile::default(false, false, false, &cmd.config_path)?;
904+
let config = ConfigFile::default(false, false, false, &cmd.config_path, &None)?;
871905
let block_post_processor = match cmd.repair_observers {
872906
Some(true) => {
873907
let tx_replayer =
@@ -891,7 +925,7 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
891925
}
892926
},
893927
Command::Db(OrdhookDbCommand::Check(cmd)) => {
894-
let config = ConfigFile::default(false, false, false, &cmd.config_path)?;
928+
let config = ConfigFile::default(false, false, false, &cmd.config_path, &None)?;
895929
{
896930
let blocks_db = open_readonly_ordhook_db_conn_rocks_db(
897931
&config.expected_cache_path(),
@@ -906,7 +940,7 @@ async fn handle_command(opts: Opts, ctx: &Context) -> Result<(), String> {
906940
}
907941
}
908942
Command::Db(OrdhookDbCommand::Drop(cmd)) => {
909-
let config = ConfigFile::default(false, false, false, &cmd.config_path)?;
943+
let config = ConfigFile::default(false, false, false, &cmd.config_path, &None)?;
910944

911945
println!(
912946
"{} blocks will be deleted. Confirm? [Y/n]",

components/ordhook-cli/src/config/file.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use ordhook::chainhook_sdk::types::{
55
};
66
use ordhook::config::{
77
Config, LogConfig, MetaProtocolsConfig, PredicatesApi, PredicatesApiConfig, ResourcesConfig,
8-
SnapshotConfig, StorageConfig, DEFAULT_BITCOIND_RPC_THREADS, DEFAULT_BITCOIND_RPC_TIMEOUT,
9-
DEFAULT_BRC20_LRU_CACHE_SIZE, DEFAULT_CONTROL_PORT, DEFAULT_MEMORY_AVAILABLE, DEFAULT_ULIMIT,
8+
SnapshotConfig, SnapshotConfigDownloadUrls, StorageConfig, DEFAULT_BITCOIND_RPC_THREADS,
9+
DEFAULT_BITCOIND_RPC_TIMEOUT, DEFAULT_BRC20_LRU_CACHE_SIZE, DEFAULT_CONTROL_PORT,
10+
DEFAULT_MEMORY_AVAILABLE, DEFAULT_ULIMIT,
1011
};
1112
use std::fs::File;
1213
use std::io::{BufReader, Read};
@@ -51,8 +52,11 @@ impl ConfigFile {
5152
};
5253

5354
let snapshot = match config_file.snapshot {
54-
Some(bootstrap) => match bootstrap.download_url {
55-
Some(ref url) => SnapshotConfig::Download(url.to_string()),
55+
Some(bootstrap) => match bootstrap.ordinals_url {
56+
Some(ref url) => SnapshotConfig::Download(SnapshotConfigDownloadUrls {
57+
ordinals: url.to_string(),
58+
brc20: bootstrap.brc20_url,
59+
}),
5660
None => SnapshotConfig::Build,
5761
},
5862
None => SnapshotConfig::Build,
@@ -144,14 +148,21 @@ impl ConfigFile {
144148
testnet: bool,
145149
mainnet: bool,
146150
config_path: &Option<String>,
151+
meta_protocols: &Option<String>,
147152
) -> Result<Config, String> {
148-
let config = match (devnet, testnet, mainnet, config_path) {
153+
let mut config = match (devnet, testnet, mainnet, config_path) {
149154
(true, false, false, _) => Config::devnet_default(),
150155
(false, true, false, _) => Config::testnet_default(),
151156
(false, false, true, _) => Config::mainnet_default(),
152157
(false, false, false, Some(config_path)) => ConfigFile::from_file_path(config_path)?,
153158
_ => Err("Invalid combination of arguments".to_string())?,
154159
};
160+
if let Some(meta_protocols) = meta_protocols {
161+
match meta_protocols.as_str() {
162+
"brc20" => config.meta_protocols.brc20 = true,
163+
_ => Err("Invalid meta protocol".to_string())?,
164+
}
165+
}
155166
Ok(config)
156167
}
157168
}
@@ -177,7 +188,8 @@ pub struct PredicatesApiConfigFile {
177188

178189
#[derive(Deserialize, Debug, Clone)]
179190
pub struct SnapshotConfigFile {
180-
pub download_url: Option<String>,
191+
pub ordinals_url: Option<String>,
192+
pub brc20_url: Option<String>,
181193
}
182194

183195
#[derive(Deserialize, Debug, Clone)]

components/ordhook-cli/src/config/generator.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ expected_observers_count = 1
3737
# Disable the following section if the state
3838
# must be built locally
3939
[snapshot]
40-
download_url = "https://archive.hiro.so/mainnet/ordhook/mainnet-ordhook-sqlite-latest"
40+
ordinals_url = "https://archive.hiro.so/mainnet/ordhook/mainnet-ordhook-sqlite-latest"
41+
brc20_url = "https://archive.hiro.so/mainnet/ordhook/mainnet-ordhook-brc20-latest"
4142
4243
[logs]
4344
ordinals_internals = true

components/ordhook-core/src/config/mod.rs

+13-17
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use std::path::PathBuf;
88

99
const DEFAULT_MAINNET_ORDINALS_SQLITE_ARCHIVE: &str =
1010
"https://archive.hiro.so/mainnet/ordhook/mainnet-ordhook-sqlite-latest";
11+
const DEFAULT_MAINNET_BRC20_SQLITE_ARCHIVE: &str =
12+
"https://archive.hiro.so/mainnet/ordhook/mainnet-ordhook-brc20-latest";
1113

1214
pub const DEFAULT_INGESTION_PORT: u16 = 20455;
1315
pub const DEFAULT_CONTROL_PORT: u16 = 20456;
@@ -56,10 +58,16 @@ pub struct PredicatesApiConfig {
5658
pub display_logs: bool,
5759
}
5860

61+
#[derive(Clone, Debug)]
62+
pub struct SnapshotConfigDownloadUrls {
63+
pub ordinals: String,
64+
pub brc20: Option<String>,
65+
}
66+
5967
#[derive(Clone, Debug)]
6068
pub enum SnapshotConfig {
6169
Build,
62-
Download(String),
70+
Download(SnapshotConfigDownloadUrls),
6371
}
6472

6573
#[derive(Clone, Debug)]
@@ -153,21 +161,6 @@ impl Config {
153161
destination_path
154162
}
155163

156-
fn expected_remote_ordinals_sqlite_base_url(&self) -> &str {
157-
match &self.snapshot {
158-
SnapshotConfig::Build => unreachable!(),
159-
SnapshotConfig::Download(url) => &url,
160-
}
161-
}
162-
163-
pub fn expected_remote_ordinals_sqlite_sha256(&self) -> String {
164-
format!("{}.sha256", self.expected_remote_ordinals_sqlite_base_url())
165-
}
166-
167-
pub fn expected_remote_ordinals_sqlite_url(&self) -> String {
168-
format!("{}.tar.gz", self.expected_remote_ordinals_sqlite_base_url())
169-
}
170-
171164
pub fn devnet_default() -> Config {
172165
Config {
173166
storage: StorageConfig {
@@ -242,7 +235,10 @@ impl Config {
242235
working_dir: default_cache_path(),
243236
},
244237
http_api: PredicatesApi::Off,
245-
snapshot: SnapshotConfig::Download(DEFAULT_MAINNET_ORDINALS_SQLITE_ARCHIVE.to_string()),
238+
snapshot: SnapshotConfig::Download(SnapshotConfigDownloadUrls {
239+
ordinals: DEFAULT_MAINNET_ORDINALS_SQLITE_ARCHIVE.to_string(),
240+
brc20: Some(DEFAULT_MAINNET_BRC20_SQLITE_ARCHIVE.to_string()),
241+
}),
246242
resources: ResourcesConfig {
247243
cpu_core_available: num_cpus::get(),
248244
memory_available: DEFAULT_MEMORY_AVAILABLE,

0 commit comments

Comments
 (0)