deserialize block only once during verification (#9161)
This commit is contained in:
parent
7d9548400d
commit
143411aaf0
@ -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
|
||||||
}),
|
}),
|
||||||
|
@ -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;
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 ¶ms.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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user