diff --git a/crates/chia-tools/src/bin/validate-blockchain-db.rs b/crates/chia-tools/src/bin/validate-blockchain-db.rs index 10a4abfaf..085b308ad 100644 --- a/crates/chia-tools/src/bin/validate-blockchain-db.rs +++ b/crates/chia-tools/src/bin/validate-blockchain-db.rs @@ -33,6 +33,10 @@ struct Args { #[arg(short, long, default_value_t = 0)] start: u32, + /// Validate blockchain against a height-to-hash file + #[arg(long)] + height_to_hash: Option, + /// Don't validate block signatures (saves time) #[arg(long, default_value_t = false)] skip_signature_validation: bool, @@ -66,7 +70,7 @@ const TESTNET11_CONSTANTS: ConsensusConstants = ConsensusConstants { agg_sig_parent_puzzle_additional_data: Bytes32::new(hex!( "54c3ed8017f77354acca4000b40424396a369740e5a504467784f392b961ab37" )), - difficulty_constant_factor: 10052721566054, + difficulty_constant_factor: 10_052_721_566_054, difficulty_starting: 30, epoch_blocks: 768, genesis_challenge: Bytes32::new(hex!( @@ -80,12 +84,12 @@ const TESTNET11_CONSTANTS: ConsensusConstants = ConsensusConstants { )), mempool_block_buffer: 10, min_plot_size: 18, - sub_slot_iters_starting: 67108864, + sub_slot_iters_starting: 67_108_864, // forks activated from the beginning on testnet11 hard_fork_height: 0, - plot_filter_128_height: 6029568, - plot_filter_64_height: 11075328, - plot_filter_32_height: 16121088, + plot_filter_128_height: 6_029_568, + plot_filter_64_height: 11_075_328, + plot_filter_32_height: 16_121_088, ..MAINNET_CONSTANTS }; @@ -129,10 +133,29 @@ features that are validated: "SELECT coin_name, coinbase, puzzle_hash, coin_parent, amount FROM coin_record WHERE confirmed_index == ?;", ) .expect("failed to prepare SQL statement finding created coins"); + let mut select_peak = connection + .prepare("SELECT hash FROM current_peak WHERE key == 0;") + .expect("failed to prepare SQL statement finding peak"); + + let mut peak_row = select_peak.query([]).expect("failed to query current peak"); + let peak_hash = peak_row + .next() + .expect("missing peak") + .expect("missing peak") + .get::<_, [u8; 32]>(0) + .expect("missing peak"); let mut prev_hash = constants.genesis_challenge; let mut prev_height: i64 = args.start as i64 - 1; + let height_to_hash: Option> = args.height_to_hash.map(|hth| { + std::fs::read(hth) + .expect("failed to read height-to-hash") + .chunks(32) + .map(|v| -> Bytes32 { v.try_into().unwrap() }) + .collect() + }); + println!("iterating over blocks starting at height {}", args.start); iterate_blocks(&args.file, args.start, None, |height, block, block_refs| { // If we don't start validation from height 0, we need to initialize the @@ -159,6 +182,11 @@ features that are validated: "at height {height} the the block height did not increment by 1, from previous block (at height {prev_height})"); prev_hash = block.header_hash(); prev_height = height as i64; + if let Some(hth) = &height_to_hash { + if hth.len() > height as usize { + assert_eq!(hth[height as usize], prev_hash, "at height {height} the block hash ({prev_hash}) does not match the height-to-hash file ({})", hth[height as usize]); + } + } let mut removals = HashSet::<[u8; 32]>::new(); // height 0 is not a transaction block so unspent coins have a // spent_index of 0 to indicate that they have not been spent. @@ -319,5 +347,6 @@ features that are validated: pool.join(); assert_eq!(pool.panic_count(), 0); + assert_eq!(peak_hash, prev_hash.as_slice()); println!("\nALL DONE, success!"); }