benches: Add block decode and validate benchmarks
What changed, and why it matters
This commit only adds new performance benchmarks to the project's benchmark suite. It does not change any production code, fix bugs, or alter security behavior. The benchmarks measure how long it takes to decode and validate Bitcoin blocks of various sizes, which is useful for future development decisions but has no direct security impact on its own.
No security action required. This is a benign benchmark-only change. Reviewers may optionally verify that the synthetic block construction and added dev-dependency are acceptable for the benchmark suite.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds benchmark functions in benches/bitcoin/block.rs and a new dependency on the bitcoin-consensus-encoding crate in benches/Cargo.toml. It introduces build_test_block() to generate synthetic blocks with configurable transaction counts and adds bench_decode_and_validate() and bench_large_block() to measure decode and decode+validate performance using Criterion. No library or consensus code is modified.
Changed components
benches/bitcoin/block.rsbenches/Cargo.tomlInspect captured patch +100 / −2
diff --git a/benches/Cargo.toml b/benches/Cargo.toml
index 3889c4bf..b1de4d98 100644
--- a/benches/Cargo.toml
+++ b/benches/Cargo.toml
@@ -9,6 +9,7 @@ edition = "2021"
bitcoin = { path = "../bitcoin", default-features = false, features = ["std"] }
bitcoin_hashes = { path = "../hashes" }
chacha20-poly1305 = { path = "../chacha20_poly1305" }
+encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encoding" }
criterion = "0.7"
hex_lit = "0.1.1"
diff --git a/benches/bitcoin/block.rs b/benches/bitcoin/block.rs
index 6ac155c7..5f95b333 100644
--- a/benches/bitcoin/block.rs
+++ b/benches/bitcoin/block.rs
@@ -3,11 +3,60 @@
use std::time::Duration;
use std::hint::black_box;
+use bitcoin::block::Header;
use bitcoin::blockdata::block::Block;
-use bitcoin::consensus::{deserialize, Decodable, Encodable};
+use bitcoin::consensus::{deserialize, serialize, Decodable, Encodable};
use bitcoin::io::sink;
+use bitcoin::script::{ScriptPubKeyBuf, ScriptSigBuf};
+use bitcoin::transaction::{OutPoint, Transaction, TxIn, TxOut, Version};
+use bitcoin::{Amount, BlockTime, CompactTarget, Sequence, TxMerkleNode, Witness};
+use encoding::decode_from_slice;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
+
+fn build_test_block(num_tx: usize) -> Vec<u8> {
+ let mut txs = Vec::with_capacity(num_tx);
+
+ // Coinbase
+ txs.push(Transaction {
+ version: Version::TWO,
+ lock_time: bitcoin::absolute::LockTime::ZERO,
+ inputs: vec![TxIn {
+ previous_output: OutPoint::COINBASE_PREVOUT,
+ script_sig: ScriptSigBuf::from_bytes(vec![0x04, 0x01, 0x00, 0x00, 0x00]),
+ sequence: Sequence::MAX,
+ witness: Witness::new(),
+ }],
+ outputs: vec![TxOut { amount: Amount::from_sat(5_000_000_000).unwrap(), script_pubkey: ScriptPubKeyBuf::new() }],
+ });
+
+ // Chain: each tx spends the previous
+ for i in 1..num_tx {
+ txs.push(Transaction {
+ version: Version::TWO,
+ lock_time: bitcoin::absolute::LockTime::ZERO,
+ inputs: vec![TxIn {
+ previous_output: OutPoint { txid: txs[i - 1].compute_txid(), vout: 0 },
+ script_sig: ScriptSigBuf::new(),
+ sequence: Sequence::MAX,
+ witness: Witness::new(),
+ }],
+ outputs: vec![TxOut { amount: Amount::from_sat_u32(1000), script_pubkey: ScriptPubKeyBuf::new() }],
+ });
+ }
+
+ let header = Header {
+ version: bitcoin::block::Version::from_consensus(1),
+ prev_blockhash: bitcoin::BlockHash::from_byte_array([0; 32]),
+ merkle_root: TxMerkleNode::from_byte_array([0; 32]),
+ time: BlockTime::from_u32(0),
+ bits: CompactTarget::from_consensus(0x1d00ffff),
+ nonce: 0,
+ };
+
+ serialize(&Block::new_unchecked(header, txs))
+}
+
fn bench_block(c: &mut Criterion) {
let raw_block = include_bytes!("../../bitcoin/tests/data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw");
assert_eq!(raw_block.len(), 1_381_836);
@@ -52,5 +101,53 @@ fn bench_block(c: &mut Criterion) {
g.finish();
}
-criterion_group!(benches, bench_block);
+fn bench_decode_and_validate(c: &mut Criterion) {
+ let raw_block = include_bytes!("../../bitcoin/tests/data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw");
+
+ let mut g = c.benchmark_group("decode_and_validate");
+ g.measurement_time(Duration::from_secs(10)).warm_up_time(Duration::from_secs(3));
+
+ g.bench_function(BenchmarkId::new("decode", "2500tx"), |b| {
+ b.iter(|| {
+ let blk: Block = decode_from_slice(&raw_block[..]).unwrap();
+ black_box(blk);
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("decode_then_validate", "2500tx"), |b| {
+ b.iter(|| {
+ let blk: Block = decode_from_slice(&raw_block[..]).unwrap();
+ black_box(blk.validate())
+ });
+ });
+
+ g.finish();
+}
+
+fn bench_large_block(c: &mut Criterion) {
+ let mut g = c.benchmark_group("large_block");
+ g.measurement_time(Duration::from_secs(15)).warm_up_time(Duration::from_secs(3));
+
+ for num_tx in [1000, 10000, 64000] {
+ let raw_block = build_test_block(num_tx);
+
+ g.bench_function(BenchmarkId::new("decode", format!("{}tx", num_tx)), |b| {
+ b.iter(|| {
+ let blk: Block = decode_from_slice(&raw_block[..]).unwrap();
+ black_box(blk);
+ });
+ });
+
+ g.bench_function(BenchmarkId::new("decode_then_validate", format!("{}tx", num_tx)), |b| {
+ b.iter(|| {
+ let blk: Block = decode_from_slice(&raw_block[..]).unwrap();
+ black_box(blk.validate())
+ });
+ });
+ }
+
+ g.finish();
+}
+
+criterion_group!(benches, bench_block, bench_decode_and_validate, bench_large_block);
criterion_main!(benches);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.