Skip to content

Commit

Permalink
Add full block data for testing and benchmarking
Browse files Browse the repository at this point in the history
- Added the block 866,342 to `testdata`, as well as the spent UTXOs
- `get_validation_flags` doesn't need to ask the chainstore for the block hash
- Changed the name of the inner `validate_block` to `validate_block_no_acc`
  • Loading branch information
JoseSK999 committed Oct 26, 2024
1 parent 6a286a6 commit 4fa51e5
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 11 deletions.
35 changes: 34 additions & 1 deletion crates/floresta-chain/benches/chain_state_bench.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::Cursor;

use bitcoin::block::Header as BlockHeader;
use bitcoin::consensus::deserialize;
use bitcoin::consensus::Decodable;
use bitcoin::Block;
use bitcoin::TxOut;
use criterion::criterion_group;
use criterion::criterion_main;
use criterion::Criterion;
Expand Down Expand Up @@ -91,10 +93,41 @@ fn connect_blocks_benchmark(c: &mut Criterion) {
});
}

fn validate_full_block_benchmark(c: &mut Criterion) {
let block_file = File::open("./testdata/block_866342/raw.zst").unwrap();
let block_bytes = zstd::decode_all(block_file).unwrap();
let block: Block = deserialize(&block_bytes).unwrap();

// Get txos spent in the block
let stxos_file = File::open("./testdata/block_866342/spent_txos.zst").unwrap();
let stxos_bytes = zstd::decode_all(stxos_file).unwrap();
let mut stxos: Vec<TxOut> =
serde_json::from_slice(&stxos_bytes).expect("Failed to deserialize JSON");

let inputs: HashMap<_, _> = block
.txdata
.iter()
.skip(1) // Skip the coinbase transaction
.flat_map(|tx| &tx.input)
.map(|txin| (txin.previous_output, stxos.remove(0)))
.collect();

let chain = setup_test_chain(Network::Bitcoin, AssumeValidArg::Disabled);

c.bench_function("validate_block_866342", |b| {
b.iter(|| {
chain
.validate_block_no_acc(&block, 866342, inputs.clone())
.unwrap()
})
});
}

criterion_group!(
benches,
accept_mainnet_headers_benchmark,
accept_headers_benchmark,
connect_blocks_benchmark
connect_blocks_benchmark,
validate_full_block_benchmark
);
criterion_main!(benches);
57 changes: 47 additions & 10 deletions crates/floresta-chain/src/pruned_utreexo/chain_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,9 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
}
#[cfg(feature = "bitcoinconsensus")]
/// Returns the validation flags, given the current block height
fn get_validation_flags(&self, height: u32) -> c_uint {
fn get_validation_flags(&self, height: u32, hash: BlockHash) -> c_uint {
let chains_params = &read_lock!(self).consensus.parameters;
let hash = read_lock!(self)
.chainstore
.get_block_hash(height)
.unwrap()
.unwrap();

if let Some(flag) = chains_params.exceptions.get(&hash) {
return *flag;
}
Expand Down Expand Up @@ -710,7 +706,12 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
}
last_block.target()
}
fn validate_block(
/// Validates the block without checking whether the inputs are present in the UTXO set. This
/// function contains the core validation logic.
///
/// The methods `BlockchainInterface::validate_block` and `UpdatableChainstate::connect_block`
/// call this and additionally verify the inclusion proof (i.e., they perform full validation).
pub fn validate_block_no_acc(
&self,
block: &Block,
height: u32,
Expand Down Expand Up @@ -738,7 +739,7 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
let subsidy = read_lock!(self).consensus.get_subsidy(height);
let verify_script = self.verify_script(height);
#[cfg(feature = "bitcoinconsensus")]
let flags = self.get_validation_flags(height);
let flags = self.get_validation_flags(height, block.header.block_hash());
#[cfg(not(feature = "bitcoinconsensus"))]
let flags = 0;
Consensus::verify_block_transactions(
Expand Down Expand Up @@ -804,7 +805,7 @@ impl<PersistedState: ChainStore> BlockchainInterface for ChainState<PersistedSta
.get_block_height(&block.block_hash())?
.ok_or(BlockchainError::BlockNotPresent)?;

self.validate_block(block, height, inputs)
self.validate_block_no_acc(block, height, inputs)
}

fn get_block_locator_for_tip(&self, tip: BlockHash) -> Result<Vec<BlockHash>, BlockchainError> {
Expand Down Expand Up @@ -1051,7 +1052,7 @@ impl<PersistedState: ChainStore> UpdatableChainstate for ChainState<PersistedSta
return Ok(height);
}

self.validate_block(block, height, inputs)?;
self.validate_block_no_acc(block, height, inputs)?;
let acc = Consensus::update_acc(&self.acc(), block, height, proof, del_hashes)?;

self.update_view(height, &block.header, acc)?;
Expand Down Expand Up @@ -1279,6 +1280,7 @@ mod test {
extern crate std;
use core::str::FromStr;
use std::format;
use std::fs::File;
use std::io::Cursor;
use std::vec::Vec;

Expand All @@ -1288,6 +1290,7 @@ mod test {
use bitcoin::hashes::hex::FromHex;
use bitcoin::Block;
use bitcoin::BlockHash;
use bitcoin::TxOut;
use rand::Rng;
use rustreexo::accumulator::proof::Proof;

Expand All @@ -1311,6 +1314,40 @@ mod test {
ChainState::new(chainstore, network, assume_valid_arg)
}

#[test]
fn test_validate_block() {
let block_file = File::open("./testdata/block_866342/raw.zst").unwrap();
let block_bytes = zstd::decode_all(block_file).unwrap();
let block: Block = deserialize(&block_bytes).unwrap();

assert_eq!(
block.block_hash(),
BlockHash::from_str("000000000000000000014ce9ba7c6760053c3c82ce6ab43d60afb101d3c8f1f1")
.unwrap(),
);

// Get txos spent in the block
let stxos_file = File::open("./testdata/block_866342/spent_txos.zst").unwrap();
let stxos_bytes = zstd::decode_all(stxos_file).unwrap();
let mut stxos: Vec<TxOut> =
serde_json::from_slice(&stxos_bytes).expect("Failed to deserialize JSON");

let inputs = block
.txdata
.iter()
.skip(1) // Skip the coinbase transaction
.flat_map(|tx| &tx.input)
.map(|txin| (txin.previous_output, stxos.remove(0)))
.collect();

assert!(stxos.is_empty(), "Moved all stxos to the inputs map");

// Check whether the block validation passes or not
let chain = setup_test_chain(Network::Bitcoin, AssumeValidArg::Disabled);
chain
.validate_block_no_acc(&block, 866342, inputs)
.expect("Block must be valid");
}
#[test]
fn accept_mainnet_headers() {
// Accepts the first 10235 mainnet headers
Expand Down
Binary file not shown.
Binary file not shown.

0 comments on commit 4fa51e5

Please sign in to comment.