From 322f47343c101a4556c946fc4c7ba383086dd234 Mon Sep 17 00:00:00 2001 From: Ludo Galabru Date: Thu, 8 Jun 2023 22:10:22 -0400 Subject: [PATCH] feat: add command to check stacks db integrity --- components/chainhook-cli/src/cli/mod.rs | 73 +++++++++++++++++++++---- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/components/chainhook-cli/src/cli/mod.rs b/components/chainhook-cli/src/cli/mod.rs index 59f623a2..ffe53c97 100644 --- a/components/chainhook-cli/src/cli/mod.rs +++ b/components/chainhook-cli/src/cli/mod.rs @@ -4,6 +4,9 @@ use crate::config::{Config, PredicatesApi}; use crate::scan::bitcoin::scan_bitcoin_chainstate_via_rpc_using_predicate; use crate::scan::stacks::scan_stacks_chainstate_via_csv_using_predicate; use crate::service::Service; +use crate::storage::{ + get_last_block_height_inserted, is_stacks_block_present, open_readonly_stacks_db_conn, +}; use chainhook_sdk::bitcoincore_rpc::{Auth, Client, RpcApi}; use chainhook_sdk::chainhooks::types::{ @@ -59,9 +62,12 @@ enum Command { /// Run a service streaming blocks and evaluating registered predicates #[clap(subcommand)] Service(ServiceCommand), - /// Explore the Ordinal Theory + /// Ordinals related subcommands #[clap(subcommand)] Hord(HordCommand), + /// Stacks related subcommands + #[clap(subcommand)] + Stacks(StacksCommand), } #[derive(Subcommand, PartialEq, Clone, Debug)] @@ -192,14 +198,14 @@ struct StartCommand { enum HordCommand { /// Db maintenance related commands #[clap(subcommand)] - Db(DbCommand), + Db(HordDbCommand), /// Db maintenance related commands #[clap(subcommand)] Scan(ScanCommand), } #[derive(Subcommand, PartialEq, Clone, Debug)] -enum DbCommand { +enum HordDbCommand { /// Rewrite hord db #[clap(name = "rewrite", bin_name = "rewrite")] Rewrite(UpdateHordDbCommand), @@ -211,7 +217,7 @@ enum DbCommand { Drop(DropHordDbCommand), /// Check integrity #[clap(name = "check", bin_name = "check")] - Check(CheckHordDbCommand), + Check(CheckDbCommand), /// Patch DB #[clap(name = "patch", bin_name = "patch")] Patch(PatchHordDbCommand), @@ -220,6 +226,20 @@ enum DbCommand { Migrate(MigrateHordDbCommand), } +#[derive(Subcommand, PartialEq, Clone, Debug)] +enum StacksCommand { + /// Db maintenance related commands + #[clap(subcommand)] + Db(StacksDbCommand), +} + +#[derive(Subcommand, PartialEq, Clone, Debug)] +enum StacksDbCommand { + /// Check integrity + #[clap(name = "check", bin_name = "check")] + Check(CheckDbCommand), +} + #[derive(Subcommand, PartialEq, Clone, Debug)] enum ScanCommand { /// Compute ordinal number of the 1st satoshi of the 1st input of a given transaction @@ -348,7 +368,7 @@ struct MigrateHordDbCommand { } #[derive(Parser, PartialEq, Clone, Debug)] -struct CheckHordDbCommand { +struct CheckDbCommand { /// Load config file path #[clap(long = "config-path")] pub config_path: Option, @@ -752,7 +772,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { } }, Command::Hord(HordCommand::Db(subcmd)) => match subcmd { - DbCommand::Sync(cmd) => { + HordDbCommand::Sync(cmd) => { let config = Config::default(false, false, false, &cmd.config_path)?; if let Some((start_block, end_block)) = should_sync_hord_db(&config, &ctx)? { if start_block == 0 { @@ -778,7 +798,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { info!(ctx.expect_logger(), "Database hord up to date"); } } - DbCommand::Rewrite(cmd) => { + HordDbCommand::Rewrite(cmd) => { let config = Config::default(false, false, false, &cmd.config_path)?; // Delete data, if any { @@ -805,7 +825,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { ) .await?; } - DbCommand::Check(cmd) => { + HordDbCommand::Check(cmd) => { let config = Config::default(false, false, false, &cmd.config_path)?; // Delete data, if any { @@ -813,7 +833,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { open_readwrite_hord_db_conn_rocks_db(&config.expected_cache_path(), &ctx)?; let mut missing_blocks = vec![]; - for i in 1..=780000 { + for i in 1..=790000 { if find_lazy_block_at_block_height(i, 3, &blocks_db_rw).is_none() { println!("Missing block {i}"); missing_blocks.push(i); @@ -822,7 +842,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { println!("{:?}", missing_blocks); } } - DbCommand::Drop(cmd) => { + HordDbCommand::Drop(cmd) => { let config = Config::default(false, false, false, &cmd.config_path)?; let blocks_db = open_readwrite_hord_db_conn_rocks_db(&config.expected_cache_path(), &ctx)?; @@ -842,13 +862,42 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> { cmd.end_block - cmd.start_block + 1 ); } - DbCommand::Patch(_cmd) => { + HordDbCommand::Patch(_cmd) => { unimplemented!() } - DbCommand::Migrate(_cmd) => { + HordDbCommand::Migrate(_cmd) => { unimplemented!() } }, + Command::Stacks(StacksCommand::Db(StacksDbCommand::Check(cmd))) => { + let config = Config::default(false, false, false, &cmd.config_path)?; + // Delete data, if any + { + let stacks_db = open_readonly_stacks_db_conn(&config.expected_cache_path(), &ctx)?; + let mut missing_blocks = vec![]; + if let Some(tip) = get_last_block_height_inserted(&stacks_db, &ctx) { + for index in 1..=tip { + let block_identifier = BlockIdentifier { + index: i, + hash: "".into(), + }; + if !is_stacks_block_present(&block_identifier, 3, &stacks_db) { + missing_blocks.push(i); + } + } + } + if missing_blocks.is_empty() { + info!(ctx.expect_logger(), "Stacks db successfully checked"); + } else { + warn!( + ctx.expect_logger(), + "Stacks db includes {} missing entries: {:?}", + missing_blocks.len(), + missing_blocks + ); + } + } + } } Ok(()) }