From f4d14e271f5a3598e5811c866f8410913127d8a3 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 11 Sep 2019 14:15:19 +0200 Subject: [PATCH] Benchmarks for block verification (#11035) * WIP * wip * Benchmarks for block verification Uses real blocks from mainnet to benchmark the `verify_*` family of methods in the `verification` module. Also exposes the `TestBlockChain` in a test helper. * Cleanup, fix CI * Bash syntax error * One more try * Fix review grumbles Revert unwanted changes Tweak CI benchmark checks --- .gitlab-ci.yml | 7 +- Cargo.lock | 3 + ethcore/types/src/verification.rs | 2 +- ethcore/verification/Cargo.toml | 13 ++ ethcore/verification/benches/8447675.rlp | Bin 0 -> 5316 bytes .../benches/8481474-parent-to-uncle.rlp | Bin 0 -> 12450 bytes ethcore/verification/benches/8481475.rlp | Bin 0 -> 32855 bytes .../benches/8481476-one-uncle.rlp | Bin 0 -> 32269 bytes ethcore/verification/benches/verification.rs | 161 ++++++++++++++++++ ethcore/verification/src/lib.rs | 5 + ethcore/verification/src/test_helpers.rs | 114 +++++++++++++ ethcore/verification/src/verification.rs | 116 ++----------- 12 files changed, 312 insertions(+), 109 deletions(-) create mode 100644 ethcore/verification/benches/8447675.rlp create mode 100644 ethcore/verification/benches/8481474-parent-to-uncle.rlp create mode 100644 ethcore/verification/benches/8481475.rlp create mode 100644 ethcore/verification/benches/8481476-one-uncle.rlp create mode 100644 ethcore/verification/benches/verification.rs create mode 100644 ethcore/verification/src/test_helpers.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 15898cc6d..469a44c98 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -100,10 +100,9 @@ cargo-check-benches: stage: test <<: *docker-cache-status script: - - time ( - cargo check --all --benches --exclude ethash --target $CARGO_TARGET --locked --verbose --color=always; - (cd ethash; time cargo check --benches --features bench --target $CARGO_TARGET --locked --verbose --color=always) - ) + - time (cargo check --all --benches --exclude ethash --exclude verification --target $CARGO_TARGET --locked --verbose --color=always) + - time (cd ethash; cargo check --benches --features bench --target $CARGO_TARGET --locked --verbose --color=always) + - time (cd ethcore/verification; cargo check --benches --features bench --target $CARGO_TARGET --locked --verbose --color=always) - sccache -s cargo-audit: diff --git a/Cargo.lock b/Cargo.lock index 7a77276ce..299662e7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4996,7 +4996,9 @@ version = "0.1.0" dependencies = [ "client-traits 0.1.0", "common-types 0.1.0", + "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "engine 0.1.0", + "ethash-engine 0.1.0", "ethcore 1.12.0", "ethcore-blockchain 0.1.0", "ethcore-call-contract 0.1.0", @@ -5014,6 +5016,7 @@ dependencies = [ "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "spec 0.1.0", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "time-utils 0.1.0", "triehash-ethereum 0.2.0", "unexpected 0.1.0", diff --git a/ethcore/types/src/verification.rs b/ethcore/types/src/verification.rs index dedf45492..c3161fe9e 100644 --- a/ethcore/types/src/verification.rs +++ b/ethcore/types/src/verification.rs @@ -57,7 +57,7 @@ impl VerificationQueueInfo { } /// An unverified block. -#[derive(PartialEq, Debug, MallocSizeOf)] +#[derive(Clone, PartialEq, Debug, MallocSizeOf)] pub struct Unverified { /// Unverified block header. pub header: Header, diff --git a/ethcore/verification/Cargo.toml b/ethcore/verification/Cargo.toml index 0a19861a4..9f7b06e4f 100644 --- a/ethcore/verification/Cargo.toml +++ b/ethcore/verification/Cargo.toml @@ -6,6 +6,10 @@ authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0" +[[bench]] +name = "verification" +harness = false + [dependencies] blockchain = { package = "ethcore-blockchain", path = "../blockchain" } call-contract = { package = "ethcore-call-contract", path = "../call-contract" } @@ -27,8 +31,17 @@ triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util unexpected = { path = "../../util/unexpected" } [dev-dependencies] +criterion = "0.3" ethcore = { path = "../", features = ["test-helpers"] } ethkey = { path = "../../accounts/ethkey" } machine = { path = "../machine" } null-engine = { path = "../engines/null-engine" } spec = { path = "../spec" } + +# Benches +ethash = { package = "ethash-engine", path = "../engines/ethash" } +tempdir = "0.3.7" + +[features] +# Used to selectively expose code for benchmarks. +bench = [] diff --git a/ethcore/verification/benches/8447675.rlp b/ethcore/verification/benches/8447675.rlp new file mode 100644 index 0000000000000000000000000000000000000000..32e50f143f5e4d8df6ea7d4089d8000ce65973ce GIT binary patch literal 5316 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 literal 0 HcmV?d00001 diff --git a/ethcore/verification/benches/8481474-parent-to-uncle.rlp b/ethcore/verification/benches/8481474-parent-to-uncle.rlp new file mode 100644 index 0000000000000000000000000000000000000000..d795f0c87d3f9fb2714196704f129e1d5bc7985f GIT binary patch literal 12450 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 Vr~XI8fGYV{=d_+3$w{2%{{u9(Lh%3q literal 0 HcmV?d00001 diff --git a/ethcore/verification/benches/8481475.rlp b/ethcore/verification/benches/8481475.rlp new file mode 100644 index 0000000000000000000000000000000000000000..ac41fd96637fc9aae73c42b75e460b9b79d6fc7c GIT binary patch literal 32855 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. + +//! benchmarking for verification + +use std::collections::BTreeMap; + +use common_types::verification::Unverified; +use criterion::{Criterion, criterion_group, criterion_main}; +use ethash::{EthashParams, Ethash}; +use ethereum_types::U256; +use ethcore::client::TestBlockChainClient; +use spec::new_constantinople_test_machine; +use tempdir::TempDir; + +use ::verification::{ + FullFamilyParams, + verification, + test_helpers::TestBlockChain, +}; + +// These are current production values. Needed when using real blocks. +fn ethash_params() -> EthashParams { + EthashParams { + minimum_difficulty: U256::from(131072), + difficulty_bound_divisor: U256::from(2048), + difficulty_increment_divisor: 10, + metropolis_difficulty_increment_divisor: 9, + duration_limit: 13, + homestead_transition: 1150000, + difficulty_hardfork_transition: u64::max_value(), + difficulty_hardfork_bound_divisor: U256::from(2048), + bomb_defuse_transition: u64::max_value(), + eip100b_transition: 4370000, + ecip1010_pause_transition: u64::max_value(), + ecip1010_continue_transition: u64::max_value(), + ecip1017_era_rounds: u64::max_value(), + block_reward: { + let mut m = BTreeMap::::new(); + m.insert(0, 5000000000000000000u64.into()); + m.insert(4370000, 3000000000000000000u64.into()); + m.insert(7280000, 2000000000000000000u64.into()); + m + }, + expip2_transition: u64::max_value(), + expip2_duration_limit: 30, + block_reward_contract_transition: 0, + block_reward_contract: None, + difficulty_bomb_delays: { + let mut m = BTreeMap::new(); + m.insert(4370000, 3000000); + m.insert(7280000, 2000000); + m + }, + progpow_transition: u64::max_value() + } +} + +fn build_ethash() -> Ethash { + let machine = new_constantinople_test_machine(); + let ethash_params = ethash_params(); + let cache_dir = TempDir::new("").unwrap(); + Ethash::new( + cache_dir.path(), + ethash_params, + machine, + None + ) +} + +fn block_verification(c: &mut Criterion) { + const PROOF: &str = "bytes from disk are ok"; + + let ethash = build_ethash(); + + // A fairly large block (32kb) with one uncle + let rlp_8481476 = include_bytes!("./8481476-one-uncle.rlp").to_vec(); + // Parent of #8481476 + let rlp_8481475 = include_bytes!("./8481475.rlp").to_vec(); + // Parent of the uncle in #8481476 + let rlp_8481474 = include_bytes!("./8481474-parent-to-uncle.rlp").to_vec(); + + // Phase 1 verification + c.bench_function("verify_block_basic", |b| { + let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF); + b.iter(|| { + assert!(verification::verify_block_basic( + &block, + ðash, + true + ).is_ok()); + }) + }); + + // Phase 2 verification + c.bench_function("verify_block_unordered", |b| { + let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF); + b.iter( || { + assert!(verification::verify_block_unordered( + block.clone(), + ðash, + true + ).is_ok()); + }) + }); + + // Phase 3 verification + let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF); + let preverified = verification::verify_block_unordered(block, ðash, true).expect(PROOF); + let parent = Unverified::from_rlp(rlp_8481475.clone()).expect(PROOF); + + // "partial" means we skip uncle and tx verification + c.bench_function("verify_block_family (partial)", |b| { + b.iter(|| { + if let Err(e) = verification::verify_block_family::( + &preverified.header, + &parent.header, + ðash, + None + ) { + panic!("verify_block_family (partial) ERROR: {:?}", e); + } + }); + }); + + let mut block_provider = TestBlockChain::new(); + block_provider.insert(rlp_8481476.clone()); // block to verify + block_provider.insert(rlp_8481475.clone()); // parent + block_provider.insert(rlp_8481474.clone()); // uncle's parent + + let client = TestBlockChainClient::default(); + c.bench_function("verify_block_family (full)", |b| { + b.iter(|| { + let full = FullFamilyParams { block: &preverified, block_provider: &block_provider, client: &client }; + if let Err(e) = verification::verify_block_family::( + &preverified.header, + &parent.header, + ðash, + Some(full), + ) { + panic!("verify_block_family (full) ERROR: {:?}", e) + } + }); + }); +} + +criterion_group!(benches, block_verification); +criterion_main!(benches); diff --git a/ethcore/verification/src/lib.rs b/ethcore/verification/src/lib.rs index 2f4be58dc..1c07e714e 100644 --- a/ethcore/verification/src/lib.rs +++ b/ethcore/verification/src/lib.rs @@ -21,11 +21,16 @@ use client_traits::BlockInfo; // The MallocSizeOf derive looks for this in the root use parity_util_mem as malloc_size_of; +#[cfg(feature = "bench" )] +pub mod verification; +#[cfg(not(feature = "bench" ))] mod verification; mod verifier; pub mod queue; mod canon_verifier; mod noop_verifier; +#[cfg(any(test, feature = "bench" ))] +pub mod test_helpers; pub use self::verification::FullFamilyParams; pub use self::verifier::Verifier; diff --git a/ethcore/verification/src/test_helpers.rs b/ethcore/verification/src/test_helpers.rs new file mode 100644 index 000000000..d8de4a0f1 --- /dev/null +++ b/ethcore/verification/src/test_helpers.rs @@ -0,0 +1,114 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Verification test helpers. + +use std::collections::HashMap; + +use blockchain::{BlockProvider, BlockChain, BlockDetails, TransactionAddress, BlockReceipts}; +use common_types::{ + BlockNumber, + encoded, + verification::Unverified, + log_entry::{LogEntry, LocalizedLogEntry}, +}; +use ethereum_types::{BloomRef, H256}; +use parity_bytes::Bytes; + +#[derive(Default)] +pub struct TestBlockChain { + blocks: HashMap, + numbers: HashMap, +} + +impl TestBlockChain { + pub fn new() -> Self { TestBlockChain::default() } + + pub fn insert(&mut self, bytes: Bytes) { + let header = Unverified::from_rlp(bytes.clone()).unwrap().header; + let hash = header.hash(); + self.blocks.insert(hash, bytes); + self.numbers.insert(header.number(), hash); + } +} + +impl BlockProvider for TestBlockChain { + fn is_known(&self, hash: &H256) -> bool { + self.blocks.contains_key(hash) + } + + fn first_block(&self) -> Option { + unimplemented!() + } + + fn best_ancient_block(&self) -> Option { + None + } + + /// Get raw block data + fn block(&self, hash: &H256) -> Option { + self.blocks.get(hash).cloned().map(encoded::Block::new) + } + + /// Get the familial details concerning a block. + fn block_details(&self, hash: &H256) -> Option { + self.blocks.get(hash).map(|bytes| { + let header = Unverified::from_rlp(bytes.to_vec()).unwrap().header; + BlockDetails { + number: header.number(), + total_difficulty: *header.difficulty(), + parent: *header.parent_hash(), + children: Vec::new(), + is_finalized: false, + } + }) + } + + /// Get the hash of given block's number. + fn block_hash(&self, index: BlockNumber) -> Option { + self.numbers.get(&index).cloned() + } + + fn transaction_address(&self, _hash: &H256) -> Option { + unimplemented!() + } + + fn block_receipts(&self, _hash: &H256) -> Option { + unimplemented!() + } + + fn block_header_data(&self, hash: &H256) -> Option { + self.block(hash) + .map(|b| b.header_view().rlp().as_raw().to_vec()) + .map(encoded::Header::new) + } + + fn block_body(&self, hash: &H256) -> Option { + self.block(hash) + .map(|b| BlockChain::block_to_body(&b.into_inner())) + .map(encoded::Body::new) + } + + fn blocks_with_bloom<'a, B, I, II>(&self, _blooms: II, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec + where BloomRef<'a>: From, II: IntoIterator + Copy, I: Iterator, Self: Sized { + unimplemented!() + } + + fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec + where F: Fn(&LogEntry) -> bool, Self: Sized { + unimplemented!() + } +} diff --git a/ethcore/verification/src/verification.rs b/ethcore/verification/src/verification.rs index f4bf48a06..f778f06e1 100644 --- a/ethcore/verification/src/verification.rs +++ b/ethcore/verification/src/verification.rs @@ -119,7 +119,12 @@ pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { } /// Phase 3 verification. Check block information against parent and uncles. -pub fn verify_block_family(header: &Header, parent: &Header, engine: &dyn Engine, do_full: Option>) -> Result<(), Error> { +pub fn verify_block_family( + header: &Header, + parent: &Header, + engine: &dyn Engine, + do_full: Option> +) -> Result<(), Error> { // TODO: verify timestamp verify_parent(&header, &parent, engine)?; engine.verify_block_family(&header, &parent)?; @@ -128,7 +133,6 @@ pub fn verify_block_family(header: &Header, parent: Some(x) => x, None => return Ok(()), }; - verify_uncles(params.block, params.block_provider, engine)?; for tx in ¶ms.block.transactions { @@ -248,7 +252,7 @@ pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> } /// Check basic header parameters. -pub fn verify_header_params(header: &Header, engine: &dyn Engine, is_full: bool, check_seal: bool) -> Result<(), Error> { +pub(crate) fn verify_header_params(header: &Header, engine: &dyn Engine, is_full: bool, check_seal: bool) -> Result<(), Error> { if check_seal { let expected_seal_fields = engine.seal_fields(header); if header.seal().len() != expected_seal_fields { @@ -306,7 +310,7 @@ pub fn verify_header_params(header: &Header, engine: &dyn Engine, is_full: bool, Ok(()) } -/// Check header parameters agains parent header. +/// Check header parameters against parent header. fn verify_parent(header: &Header, parent: &Header, engine: &dyn Engine) -> Result<(), Error> { assert!(header.parent_hash().is_zero() || &parent.hash() == header.parent_hash(), "Parent hash should already have been verified; qed"); @@ -364,11 +368,10 @@ fn verify_block_integrity(block: &Unverified) -> Result<(), Error> { mod tests { use super::*; - use std::collections::{BTreeMap, HashMap}; + use std::collections::BTreeMap; use std::time::{SystemTime, UNIX_EPOCH}; - use ethereum_types::{H256, BloomRef, U256, Address}; - use blockchain::{BlockDetails, TransactionAddress, BlockReceipts}; + use ethereum_types::{H256, U256, Address}; use parity_bytes::Bytes; use keccak_hash::keccak; use engine::Engine; @@ -379,17 +382,17 @@ mod tests { test_helpers::{create_test_block_with_data, create_test_block} }; use common_types::{ - encoded, engines::params::CommonParams, errors::BlockError::*, transaction::{SignedTransaction, Transaction, UnverifiedTransaction, Action}, - log_entry::{LogEntry, LocalizedLogEntry}, }; use rlp; use triehash::ordered_trie_root; use machine::Machine; use null_engine::NullEngine; + use crate::test_helpers::TestBlockChain; + fn check_ok(result: Result<(), Error>) { result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); } @@ -412,101 +415,6 @@ mod tests { } } - struct TestBlockChain { - blocks: HashMap, - numbers: HashMap, - } - - impl Default for TestBlockChain { - fn default() -> Self { - TestBlockChain::new() - } - } - - impl TestBlockChain { - pub fn new() -> Self { - TestBlockChain { - blocks: HashMap::new(), - numbers: HashMap::new(), - } - } - - pub fn insert(&mut self, bytes: Bytes) { - let header = Unverified::from_rlp(bytes.clone()).unwrap().header; - let hash = header.hash(); - self.blocks.insert(hash, bytes); - self.numbers.insert(header.number(), hash); - } - } - - impl BlockProvider for TestBlockChain { - fn is_known(&self, hash: &H256) -> bool { - self.blocks.contains_key(hash) - } - - fn first_block(&self) -> Option { - unimplemented!() - } - - /// Get raw block data - fn block(&self, hash: &H256) -> Option { - self.blocks.get(hash).cloned().map(encoded::Block::new) - } - - fn block_header_data(&self, hash: &H256) -> Option { - self.block(hash) - .map(|b| b.header_view().rlp().as_raw().to_vec()) - .map(encoded::Header::new) - } - - fn block_body(&self, hash: &H256) -> Option { - self.block(hash) - .map(|b| BlockChain::block_to_body(&b.into_inner())) - .map(encoded::Body::new) - } - - fn best_ancient_block(&self) -> Option { - None - } - - /// Get the familial details concerning a block. - fn block_details(&self, hash: &H256) -> Option { - self.blocks.get(hash).map(|bytes| { - let header = Unverified::from_rlp(bytes.to_vec()).unwrap().header; - BlockDetails { - number: header.number(), - total_difficulty: *header.difficulty(), - parent: *header.parent_hash(), - children: Vec::new(), - is_finalized: false, - } - }) - } - - fn transaction_address(&self, _hash: &H256) -> Option { - unimplemented!() - } - - /// Get the hash of given block's number. - fn block_hash(&self, index: BlockNumber) -> Option { - self.numbers.get(&index).cloned() - } - - fn block_receipts(&self, _hash: &H256) -> Option { - unimplemented!() - } - - fn blocks_with_bloom<'a, B, I, II>(&self, _blooms: II, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec - where BloomRef<'a>: From, II: IntoIterator + Copy, I: Iterator, Self: Sized { - unimplemented!() - } - - fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec - where F: Fn(&LogEntry) -> bool, Self: Sized { - unimplemented!() - } - } - fn basic_test(bytes: &[u8], engine: &dyn Engine) -> Result<(), Error> { let unverified = Unverified::from_rlp(bytes.to_vec())?; verify_block_basic(&unverified, engine, true)