feat: add command to check stacks db integrity

This commit is contained in:
Ludo Galabru
2023-06-08 22:10:22 -04:00
parent 6dacc3a42d
commit 322f47343c

View File

@@ -4,6 +4,9 @@ use crate::config::{Config, PredicatesApi};
use crate::scan::bitcoin::scan_bitcoin_chainstate_via_rpc_using_predicate; use crate::scan::bitcoin::scan_bitcoin_chainstate_via_rpc_using_predicate;
use crate::scan::stacks::scan_stacks_chainstate_via_csv_using_predicate; use crate::scan::stacks::scan_stacks_chainstate_via_csv_using_predicate;
use crate::service::Service; 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::bitcoincore_rpc::{Auth, Client, RpcApi};
use chainhook_sdk::chainhooks::types::{ use chainhook_sdk::chainhooks::types::{
@@ -59,9 +62,12 @@ enum Command {
/// Run a service streaming blocks and evaluating registered predicates /// Run a service streaming blocks and evaluating registered predicates
#[clap(subcommand)] #[clap(subcommand)]
Service(ServiceCommand), Service(ServiceCommand),
/// Explore the Ordinal Theory /// Ordinals related subcommands
#[clap(subcommand)] #[clap(subcommand)]
Hord(HordCommand), Hord(HordCommand),
/// Stacks related subcommands
#[clap(subcommand)]
Stacks(StacksCommand),
} }
#[derive(Subcommand, PartialEq, Clone, Debug)] #[derive(Subcommand, PartialEq, Clone, Debug)]
@@ -192,14 +198,14 @@ struct StartCommand {
enum HordCommand { enum HordCommand {
/// Db maintenance related commands /// Db maintenance related commands
#[clap(subcommand)] #[clap(subcommand)]
Db(DbCommand), Db(HordDbCommand),
/// Db maintenance related commands /// Db maintenance related commands
#[clap(subcommand)] #[clap(subcommand)]
Scan(ScanCommand), Scan(ScanCommand),
} }
#[derive(Subcommand, PartialEq, Clone, Debug)] #[derive(Subcommand, PartialEq, Clone, Debug)]
enum DbCommand { enum HordDbCommand {
/// Rewrite hord db /// Rewrite hord db
#[clap(name = "rewrite", bin_name = "rewrite")] #[clap(name = "rewrite", bin_name = "rewrite")]
Rewrite(UpdateHordDbCommand), Rewrite(UpdateHordDbCommand),
@@ -211,7 +217,7 @@ enum DbCommand {
Drop(DropHordDbCommand), Drop(DropHordDbCommand),
/// Check integrity /// Check integrity
#[clap(name = "check", bin_name = "check")] #[clap(name = "check", bin_name = "check")]
Check(CheckHordDbCommand), Check(CheckDbCommand),
/// Patch DB /// Patch DB
#[clap(name = "patch", bin_name = "patch")] #[clap(name = "patch", bin_name = "patch")]
Patch(PatchHordDbCommand), Patch(PatchHordDbCommand),
@@ -220,6 +226,20 @@ enum DbCommand {
Migrate(MigrateHordDbCommand), 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)] #[derive(Subcommand, PartialEq, Clone, Debug)]
enum ScanCommand { enum ScanCommand {
/// Compute ordinal number of the 1st satoshi of the 1st input of a given transaction /// 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)] #[derive(Parser, PartialEq, Clone, Debug)]
struct CheckHordDbCommand { struct CheckDbCommand {
/// Load config file path /// Load config file path
#[clap(long = "config-path")] #[clap(long = "config-path")]
pub config_path: Option<String>, pub config_path: Option<String>,
@@ -752,7 +772,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> {
} }
}, },
Command::Hord(HordCommand::Db(subcmd)) => match subcmd { Command::Hord(HordCommand::Db(subcmd)) => match subcmd {
DbCommand::Sync(cmd) => { HordDbCommand::Sync(cmd) => {
let config = Config::default(false, false, false, &cmd.config_path)?; let config = Config::default(false, false, false, &cmd.config_path)?;
if let Some((start_block, end_block)) = should_sync_hord_db(&config, &ctx)? { if let Some((start_block, end_block)) = should_sync_hord_db(&config, &ctx)? {
if start_block == 0 { 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"); 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)?; let config = Config::default(false, false, false, &cmd.config_path)?;
// Delete data, if any // Delete data, if any
{ {
@@ -805,7 +825,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> {
) )
.await?; .await?;
} }
DbCommand::Check(cmd) => { HordDbCommand::Check(cmd) => {
let config = Config::default(false, false, false, &cmd.config_path)?; let config = Config::default(false, false, false, &cmd.config_path)?;
// Delete data, if any // 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)?; open_readwrite_hord_db_conn_rocks_db(&config.expected_cache_path(), &ctx)?;
let mut missing_blocks = vec![]; 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() { if find_lazy_block_at_block_height(i, 3, &blocks_db_rw).is_none() {
println!("Missing block {i}"); println!("Missing block {i}");
missing_blocks.push(i); missing_blocks.push(i);
@@ -822,7 +842,7 @@ async fn handle_command(opts: Opts, ctx: Context) -> Result<(), String> {
println!("{:?}", missing_blocks); println!("{:?}", missing_blocks);
} }
} }
DbCommand::Drop(cmd) => { HordDbCommand::Drop(cmd) => {
let config = Config::default(false, false, false, &cmd.config_path)?; let config = Config::default(false, false, false, &cmd.config_path)?;
let blocks_db = let blocks_db =
open_readwrite_hord_db_conn_rocks_db(&config.expected_cache_path(), &ctx)?; 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 cmd.end_block - cmd.start_block + 1
); );
} }
DbCommand::Patch(_cmd) => { HordDbCommand::Patch(_cmd) => {
unimplemented!() unimplemented!()
} }
DbCommand::Migrate(_cmd) => { HordDbCommand::Migrate(_cmd) => {
unimplemented!() 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(()) Ok(())
} }