deserialize block only once during verification (#9161)

This commit is contained in:
Marek Kotewicz 2018-07-25 14:36:46 +02:00 committed by GitHub
parent 7d9548400d
commit 143411aaf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 71 deletions

View File

@ -73,15 +73,14 @@ use transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTrans
use types::filter::Filter; use types::filter::Filter;
use types::ancestry_action::AncestryAction; use types::ancestry_action::AncestryAction;
use verification; use verification;
use verification::{PreverifiedBlock, Verifier}; use verification::{PreverifiedBlock, Verifier, BlockQueue};
use verification::queue::BlockQueue;
use views::BlockView; use views::BlockView;
// re-export // re-export
pub use types::blockchain_info::BlockChainInfo; pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus; pub use types::block_status::BlockStatus;
pub use blockchain::CacheSize as BlockChainCacheSize; pub use blockchain::CacheSize as BlockChainCacheSize;
pub use verification::queue::QueueInfo as BlockQueueInfo; pub use verification::QueueInfo as BlockQueueInfo;
use_contract!(registry, "Registry", "res/contracts/registrar.json"); use_contract!(registry, "Registry", "res/contracts/registrar.json");
@ -371,8 +370,7 @@ impl Importer {
&parent, &parent,
engine, engine,
Some(verification::FullFamilyParams { Some(verification::FullFamilyParams {
block_bytes: &block.bytes, block: &block,
transactions: &block.transactions,
block_provider: &**chain, block_provider: &**chain,
client client
}), }),

View File

@ -16,8 +16,8 @@
//! Block verification utilities. //! Block verification utilities.
pub mod verification; mod verification;
pub mod verifier; mod verifier;
pub mod queue; pub mod queue;
mod canon_verifier; mod canon_verifier;
mod noop_verifier; mod noop_verifier;

View File

@ -72,6 +72,7 @@ pub mod blocks {
use error::{Error, ErrorKind, BlockError}; use error::{Error, ErrorKind, BlockError};
use header::Header; use header::Header;
use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered}; use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered};
use transaction::UnverifiedTransaction;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
@ -86,7 +87,7 @@ pub mod blocks {
type Verified = PreverifiedBlock; type Verified = PreverifiedBlock;
fn create(input: Self::Input, engine: &EthEngine) -> Result<Self::Unverified, Error> { fn create(input: Self::Input, engine: &EthEngine) -> Result<Self::Unverified, Error> {
match verify_block_basic(&input.header, &input.bytes, engine) { match verify_block_basic(&input, engine) {
Ok(()) => Ok(input), Ok(()) => Ok(input),
Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(oob)), _)) => { Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(oob)), _)) => {
debug!(target: "client", "Block received too early {}: {:?}", input.hash(), oob); debug!(target: "client", "Block received too early {}: {:?}", input.hash(), oob);
@ -101,7 +102,7 @@ pub mod blocks {
fn verify(un: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result<Self::Verified, Error> { fn verify(un: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result<Self::Verified, Error> {
let hash = un.hash(); let hash = un.hash();
match verify_block_unordered(un.header, un.bytes, engine, check_seal) { match verify_block_unordered(un, engine, check_seal) {
Ok(verified) => Ok(verified), Ok(verified) => Ok(verified),
Err(e) => { Err(e) => {
warn!(target: "client", "Stage 2 block verification failed for {}: {:?}", hash, e); warn!(target: "client", "Stage 2 block verification failed for {}: {:?}", hash, e);
@ -113,25 +114,43 @@ pub mod blocks {
/// An unverified block. /// An unverified block.
pub struct Unverified { pub struct Unverified {
header: Header, /// Unverified block header.
bytes: Bytes, pub header: Header,
/// Unverified block transactions.
pub transactions: Vec<UnverifiedTransaction>,
/// Unverified block uncles.
pub uncles: Vec<Header>,
/// Raw block bytes.
pub bytes: Bytes,
} }
impl Unverified { impl Unverified {
/// Create an `Unverified` from raw bytes. /// Create an `Unverified` from raw bytes.
pub fn from_rlp(bytes: Bytes) -> Result<Self, ::rlp::DecoderError> { pub fn from_rlp(bytes: Bytes) -> Result<Self, ::rlp::DecoderError> {
use rlp::Rlp;
let (header, transactions, uncles) = {
let rlp = Rlp::new(&bytes);
let header = rlp.val_at(0)?;
let transactions = rlp.list_at(1)?;
let uncles = rlp.list_at(2)?;
(header, transactions, uncles)
};
let header = ::rlp::Rlp::new(&bytes).val_at(0)?;
Ok(Unverified { Ok(Unverified {
header: header, header,
bytes: bytes, transactions,
uncles,
bytes,
}) })
} }
} }
impl HeapSizeOf for Unverified { impl HeapSizeOf for Unverified {
fn heap_size_of_children(&self) -> usize { fn heap_size_of_children(&self) -> usize {
self.header.heap_size_of_children() + self.bytes.heap_size_of_children() self.header.heap_size_of_children()
+ self.transactions.heap_size_of_children()
+ self.uncles.heap_size_of_children()
+ self.bytes.heap_size_of_children()
} }
} }

View File

@ -25,7 +25,6 @@ use std::collections::HashSet;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use bytes::Bytes; use bytes::Bytes;
use ethereum_types::H256;
use hash::keccak; use hash::keccak;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use rlp::Rlp; use rlp::Rlp;
@ -37,8 +36,8 @@ use client::{BlockInfo, CallContract};
use engines::EthEngine; use engines::EthEngine;
use error::{BlockError, Error}; use error::{BlockError, Error};
use header::{BlockNumber, Header}; use header::{BlockNumber, Header};
use transaction::{SignedTransaction, UnverifiedTransaction}; use transaction::SignedTransaction;
use views::BlockView; use verification::queue::kind::blocks::Unverified;
/// Preprocessed block data gathered in `verify_block_unordered` call /// Preprocessed block data gathered in `verify_block_unordered` call
pub struct PreverifiedBlock { pub struct PreverifiedBlock {
@ -46,6 +45,8 @@ pub struct PreverifiedBlock {
pub header: Header, pub header: Header,
/// Populated block transactions /// Populated block transactions
pub transactions: Vec<SignedTransaction>, pub transactions: Vec<SignedTransaction>,
/// Populated block uncles
pub uncles: Vec<Header>,
/// Block bytes /// Block bytes
pub bytes: Bytes, pub bytes: Bytes,
} }
@ -59,63 +60,66 @@ impl HeapSizeOf for PreverifiedBlock {
} }
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { pub fn verify_block_basic(block: &Unverified, engine: &EthEngine) -> Result<(), Error> {
verify_header_params(&header, engine, true)?; verify_header_params(&block.header, engine, true)?;
verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash())?; verify_block_integrity(block)?;
engine.verify_block_basic(&header)?; engine.verify_block_basic(&block.header)?;
for u in Rlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) {
let u = u?; for uncle in &block.uncles {
verify_header_params(&u, engine, false)?; verify_header_params(uncle, engine, false)?;
engine.verify_block_basic(&u)?; engine.verify_block_basic(uncle)?;
} }
for t in Rlp::new(bytes).at(1)?.iter().map(|rlp| rlp.as_val::<UnverifiedTransaction>()) { for t in &block.transactions {
engine.verify_transaction_basic(&t?, &header)?; engine.verify_transaction_basic(t, &block.header)?;
} }
Ok(()) Ok(())
} }
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
/// Still operates on a individual block /// Still operates on a individual block
/// Returns a `PreverifiedBlock` structure populated with transactions /// Returns a `PreverifiedBlock` structure populated with transactions
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &EthEngine, check_seal: bool) -> Result<PreverifiedBlock, Error> { pub fn verify_block_unordered(block: Unverified, engine: &EthEngine, check_seal: bool) -> Result<PreverifiedBlock, Error> {
let header = block.header;
if check_seal { if check_seal {
engine.verify_block_unordered(&header)?; engine.verify_block_unordered(&header)?;
for u in Rlp::new(&bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) { for uncle in &block.uncles {
engine.verify_block_unordered(&u?)?; engine.verify_block_unordered(uncle)?;
} }
} }
// Verify transactions. // Verify transactions.
let mut transactions = Vec::new();
let nonce_cap = if header.number() >= engine.params().dust_protection_transition { let nonce_cap = if header.number() >= engine.params().dust_protection_transition {
Some((engine.params().nonce_cap_increment * header.number()).into()) Some((engine.params().nonce_cap_increment * header.number()).into())
} else { None }; } else {
{ None
let v = view!(BlockView, &bytes); };
for t in v.transactions() {
let transactions = block.transactions
.into_iter()
.map(|t| {
let t = engine.verify_transaction_unordered(t, &header)?; let t = engine.verify_transaction_unordered(t, &header)?;
if let Some(max_nonce) = nonce_cap { if let Some(max_nonce) = nonce_cap {
if t.nonce >= max_nonce { if t.nonce >= max_nonce {
return Err(BlockError::TooManyTransactions(t.sender()).into()); return Err(BlockError::TooManyTransactions(t.sender()).into());
} }
} }
transactions.push(t); Ok(t)
} })
} .collect::<Result<Vec<_>, Error>>()?;
Ok(PreverifiedBlock { Ok(PreverifiedBlock {
header: header, header,
transactions: transactions, transactions,
bytes: bytes, uncles: block.uncles,
bytes: block.bytes,
}) })
} }
/// Parameters for full verification of block family /// Parameters for full verification of block family
pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> {
/// Serialized block bytes /// Preverified block
pub block_bytes: &'a [u8], pub block: &'a PreverifiedBlock,
/// Signed transactions
pub transactions: &'a [SignedTransaction],
/// Block provider to use during verification /// Block provider to use during verification
pub block_provider: &'a BlockProvider, pub block_provider: &'a BlockProvider,
@ -135,17 +139,18 @@ pub fn verify_block_family<C: BlockInfo + CallContract>(header: &Header, parent:
None => return Ok(()), None => return Ok(()),
}; };
verify_uncles(header, params.block_bytes, params.block_provider, engine)?; verify_uncles(params.block, params.block_provider, engine)?;
for transaction in params.transactions { for tx in &params.block.transactions {
engine.machine().verify_transaction(transaction, header, params.client)?; engine.machine().verify_transaction(tx, header, params.client)?;
} }
Ok(()) Ok(())
} }
fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &EthEngine) -> Result<(), Error> { fn verify_uncles(block: &PreverifiedBlock, bc: &BlockProvider, engine: &EthEngine) -> Result<(), Error> {
let num_uncles = Rlp::new(bytes).at(2)?.item_count()?; let header = &block.header;
let num_uncles = block.uncles.len();
let max_uncles = engine.maximum_uncle_count(header.number()); let max_uncles = engine.maximum_uncle_count(header.number());
if num_uncles != 0 { if num_uncles != 0 {
if num_uncles > max_uncles { if num_uncles > max_uncles {
@ -174,8 +179,7 @@ fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &Eth
} }
let mut verified = HashSet::new(); let mut verified = HashSet::new();
for uncle in Rlp::new(bytes).at(2)?.iter().map(|rlp| rlp.as_val::<Header>()) { for uncle in &block.uncles {
let uncle = uncle?;
if excluded.contains(&uncle.hash()) { if excluded.contains(&uncle.hash()) {
return Err(From::from(BlockError::UncleInChain(uncle.hash()))) return Err(From::from(BlockError::UncleInChain(uncle.hash())))
} }
@ -332,16 +336,22 @@ fn verify_parent(header: &Header, parent: &Header, engine: &EthEngine) -> Result
} }
/// Verify block data against header: transactions root and uncles hash. /// Verify block data against header: transactions root and uncles hash.
fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> { fn verify_block_integrity(block: &Unverified) -> Result<(), Error> {
let block = Rlp::new(block); let block_rlp = Rlp::new(&block.bytes);
let tx = block.at(1)?; let tx = block_rlp.at(1)?;
let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw())); let expected_root = ordered_trie_root(tx.iter().map(|r| r.as_raw()));
if expected_root != transactions_root { if &expected_root != block.header.transactions_root() {
return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() }))) bail!(BlockError::InvalidTransactionsRoot(Mismatch {
expected: expected_root,
found: *block.header.transactions_root(),
}));
} }
let expected_uncles = &keccak(block.at(2)?.as_raw()); let expected_uncles = keccak(block_rlp.at(2)?.as_raw());
if expected_uncles != uncles_hash { if &expected_uncles != block.header.uncles_hash(){
return Err(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() }))) bail!(BlockError::InvalidUnclesHash(Mismatch {
expected: expected_uncles,
found: *block.header.uncles_hash(),
}));
} }
Ok(()) Ok(())
} }
@ -366,6 +376,7 @@ mod tests {
use types::log_entry::{LogEntry, LocalizedLogEntry}; use types::log_entry::{LogEntry, LocalizedLogEntry};
use rlp; use rlp;
use triehash::ordered_trie_root; use triehash::ordered_trie_root;
use views::BlockView;
fn check_ok(result: Result<(), Error>) { fn check_ok(result: Result<(), Error>) {
result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e));
@ -486,8 +497,8 @@ mod tests {
} }
fn basic_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { fn basic_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> {
let header = view!(BlockView, bytes).header(); let unverified = Unverified::from_rlp(bytes.to_vec())?;
verify_block_basic(&header, bytes, engine) verify_block_basic(&unverified, engine)
} }
fn family_test<BC>(bytes: &[u8], engine: &EthEngine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { fn family_test<BC>(bytes: &[u8], engine: &EthEngine, bc: &BC) -> Result<(), Error> where BC: BlockProvider {
@ -507,18 +518,25 @@ mod tests {
.ok_or(BlockError::UnknownParent(header.parent_hash().clone()))? .ok_or(BlockError::UnknownParent(header.parent_hash().clone()))?
.decode()?; .decode()?;
let block = PreverifiedBlock {
header,
transactions,
uncles: view.uncles(),
bytes: bytes.to_vec(),
};
let full_params = FullFamilyParams { let full_params = FullFamilyParams {
block_bytes: bytes, block: &block,
transactions: &transactions[..],
block_provider: bc as &BlockProvider, block_provider: bc as &BlockProvider,
client: &client, client: &client,
}; };
verify_block_family(&header, &parent, engine, Some(full_params)) verify_block_family(&block.header, &parent, engine, Some(full_params))
} }
fn unordered_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { fn unordered_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> {
let header = view!(BlockView, bytes).header(); use verification::queue::kind::blocks::Unverified;
verify_block_unordered(header, bytes.to_vec(), engine, false)?; let un = Unverified::from_rlp(bytes.to_vec())?;
verify_block_unordered(un, engine, false)?;
Ok(()) Ok(())
} }

View File

@ -282,6 +282,12 @@ pub struct UnverifiedTransaction {
hash: H256, hash: H256,
} }
impl HeapSizeOf for UnverifiedTransaction {
fn heap_size_of_children(&self) -> usize {
self.unsigned.heap_size_of_children()
}
}
impl Deref for UnverifiedTransaction { impl Deref for UnverifiedTransaction {
type Target = Transaction; type Target = Transaction;
@ -436,7 +442,7 @@ pub struct SignedTransaction {
impl HeapSizeOf for SignedTransaction { impl HeapSizeOf for SignedTransaction {
fn heap_size_of_children(&self) -> usize { fn heap_size_of_children(&self) -> usize {
self.transaction.unsigned.heap_size_of_children() self.transaction.heap_size_of_children()
} }
} }