More block verifications
This commit is contained in:
parent
28a658015d
commit
af21038bb9
@ -1,6 +1,7 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use block::Block;
|
use block::Block;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
|
use verification::VerificationError;
|
||||||
|
|
||||||
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
||||||
/// Provides hooks into each of the major parts of block import.
|
/// Provides hooks into each of the major parts of block import.
|
||||||
@ -25,7 +26,7 @@ pub trait Engine {
|
|||||||
fn evm_schedule(&self, env_info: &EnvInfo) -> EvmSchedule;
|
fn evm_schedule(&self, env_info: &EnvInfo) -> EvmSchedule;
|
||||||
|
|
||||||
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
||||||
fn maximum_extra_data_size(&self, _env_info: &EnvInfo) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
||||||
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
|
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
|
||||||
|
|
||||||
/// Block transformation functions, before and after the transactions.
|
/// Block transformation functions, before and after the transactions.
|
||||||
@ -36,12 +37,12 @@ pub trait Engine {
|
|||||||
/// `parent` (the parent header) and `block` (the header's full block) may be provided for additional
|
/// `parent` (the parent header) and `block` (the header's full block) may be provided for additional
|
||||||
/// checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
/// checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||||
// TODO: consider including State in the params.
|
// TODO: consider including State in the params.
|
||||||
fn verify_block(&self, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), EthcoreError> { Ok(()) }
|
fn verify_block(&self, _mode: VerificationMode, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), VerificationError> { Ok(()) }
|
||||||
|
|
||||||
/// Additional verification for transactions in blocks.
|
/// Additional verification for transactions in blocks.
|
||||||
// TODO: Add flags for which bits of the transaction to check.
|
// TODO: Add flags for which bits of the transaction to check.
|
||||||
// TODO: consider including State in the params.
|
// TODO: consider including State in the params.
|
||||||
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), EthcoreError> { Ok(()) }
|
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), VerificationError> { Ok(()) }
|
||||||
|
|
||||||
/// Don't forget to call Super::populateFromParent when subclassing & overriding.
|
/// Don't forget to call Super::populateFromParent when subclassing & overriding.
|
||||||
// TODO: consider including State in the params.
|
// TODO: consider including State in the params.
|
||||||
@ -55,3 +56,11 @@ pub trait Engine {
|
|||||||
|
|
||||||
// TODO: sealing stuff - though might want to leave this for later.
|
// TODO: sealing stuff - though might want to leave this for later.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum VerificationMode {
|
||||||
|
/// Do a quick and basic verification if possible.
|
||||||
|
Quick,
|
||||||
|
/// Do a full verification.
|
||||||
|
Full
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ use common::*;
|
|||||||
use block::*;
|
use block::*;
|
||||||
use spec::*;
|
use spec::*;
|
||||||
use engine::*;
|
use engine::*;
|
||||||
|
use verification::*;
|
||||||
|
|
||||||
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
|
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
|
||||||
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
||||||
@ -25,6 +26,91 @@ impl Engine for Ethash {
|
|||||||
let a = block.header().author.clone();
|
let a = block.header().author.clone();
|
||||||
block.state_mut().add_balance(&a, &decode(&self.spec().engine_params.get("block_reward").unwrap()));
|
block.state_mut().add_balance(&a, &decode(&self.spec().engine_params.get("block_reward").unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_block(&self, mode: VerificationMode, header: &Header, parent: Option<&Header>, block: Option<&[u8]>) -> Result<(), VerificationError> {
|
||||||
|
if mode == VerificationMode::Quick {
|
||||||
|
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
|
if header.difficulty < min_difficulty {
|
||||||
|
return Err(VerificationError::block(
|
||||||
|
BlockVerificationError::InvalidDifficulty { required: min_difficulty, got: header.difficulty },
|
||||||
|
block.map(|b| b.to_vec())));
|
||||||
|
}
|
||||||
|
let min_gas_limit = decode(self.spec().engine_params.get("minGasLimit").unwrap());
|
||||||
|
if header.gas_limit < min_gas_limit {
|
||||||
|
return Err(VerificationError::block(
|
||||||
|
BlockVerificationError::InvalidGasLimit { min: min_gas_limit, max: From::from(0), got: header.gas_limit },
|
||||||
|
block.map(|b| b.to_vec())));
|
||||||
|
}
|
||||||
|
let len: U256 = From::from(header.extra_data.len());
|
||||||
|
let maximum_extra_data_size: U256 = From::from(self.maximum_extra_data_size());
|
||||||
|
if header.number != From::from(0) && len > maximum_extra_data_size {
|
||||||
|
return Err(VerificationError::block(
|
||||||
|
BlockVerificationError::ExtraDataTooBig { required: maximum_extra_data_size, got: len },
|
||||||
|
block.map(|b| b.to_vec())));
|
||||||
|
}
|
||||||
|
match parent {
|
||||||
|
Some(p) => {
|
||||||
|
// Check difficulty is correct given the two timestamps.
|
||||||
|
let expected_difficulty = self.calculate_difficuty(header, p);
|
||||||
|
if header.difficulty != expected_difficulty {
|
||||||
|
return Err(VerificationError::block(
|
||||||
|
BlockVerificationError::InvalidDifficulty { required: expected_difficulty, got: header.difficulty },
|
||||||
|
block.map(|b| b.to_vec())));
|
||||||
|
}
|
||||||
|
let gas_limit_divisor = decode(self.spec().engine_params.get("gasLimitBoundDivisor").unwrap());
|
||||||
|
let min_gas = p.gas_limit - p.gas_limit / gas_limit_divisor;
|
||||||
|
let max_gas = p.gas_limit + p.gas_limit / gas_limit_divisor;
|
||||||
|
if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
|
||||||
|
return Err(VerificationError::block(
|
||||||
|
BlockVerificationError::InvalidGasLimit { min: min_gas_limit, max: max_gas, got: header.gas_limit },
|
||||||
|
block.map(|b| b.to_vec())));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
// TODO: Verify seal
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), VerificationError> { Ok(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ethash {
|
||||||
|
fn calculate_difficuty(&self, header: &Header, parent: &Header) -> U256 {
|
||||||
|
const EXP_DIFF_PERIOD: u64 = 100000;
|
||||||
|
if header.number == From::from(0) {
|
||||||
|
panic!("Can't calculate genesis block difficulty");
|
||||||
|
}
|
||||||
|
|
||||||
|
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
|
let difficulty_bound_divisor = decode(self.spec().engine_params.get("difficultyBoundDivisor").unwrap());
|
||||||
|
let duration_limit = decode(self.spec().engine_params.get("durationLimit").unwrap());
|
||||||
|
let frontier_limit = decode(self.spec().engine_params.get("frontierCompatibilityModeLimit").unwrap());
|
||||||
|
let mut target = if header.number < frontier_limit {
|
||||||
|
if header.timestamp >= parent.timestamp + duration_limit {
|
||||||
|
parent.difficulty - (parent.difficulty / difficulty_bound_divisor)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parent.difficulty + (parent.difficulty / difficulty_bound_divisor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let diff_inc = (header.timestamp - parent.timestamp) / From::from(10);
|
||||||
|
if diff_inc <= From::from(1) {
|
||||||
|
parent.difficulty + parent.difficulty / From::from(2048) * (U256::from(1) - diff_inc)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parent.difficulty - parent.difficulty / From::from(2048) * max(diff_inc - From::from(1), From::from(99))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
target = max(min_difficulty, target);
|
||||||
|
let period = ((parent.number + From::from(1)).as_u64() / EXP_DIFF_PERIOD) as usize;
|
||||||
|
if period > 1 {
|
||||||
|
target = max(min_difficulty, target + (U256::from(1) << (period - 2)));
|
||||||
|
}
|
||||||
|
target
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: test for on_close_block.
|
// TODO: test for on_close_block.
|
||||||
@ -44,3 +130,4 @@ fn playpen() {
|
|||||||
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]);
|
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]);
|
||||||
// let c = b.close();
|
// let c = b.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,4 +102,5 @@ pub mod extras;
|
|||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod verification;
|
||||||
pub mod ethereum;
|
pub mod ethereum;
|
||||||
|
@ -1,10 +1,35 @@
|
|||||||
use util::uint::*;
|
use util::*;
|
||||||
use util::hash::*;
|
|
||||||
use util::rlp::*;
|
|
||||||
use util::sha3::Hashable;
|
|
||||||
use util::triehash::ordered_trie_root;
|
|
||||||
use header::Header;
|
use header::Header;
|
||||||
use client::BlockNumber;
|
use client::BlockNumber;
|
||||||
|
use engine::{Engine, VerificationMode};
|
||||||
|
use views::BlockView;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VerificationError {
|
||||||
|
pub block: Option<Bytes>,
|
||||||
|
pub error: VerificationErrorOption,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerificationError {
|
||||||
|
pub fn block(error: BlockVerificationError, block: Option<Bytes>) -> VerificationError {
|
||||||
|
VerificationError {
|
||||||
|
block: block,
|
||||||
|
error: VerificationErrorOption::Block(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn transaction(error: TransactionVerificationError, block: Option<Bytes>) -> VerificationError {
|
||||||
|
VerificationError {
|
||||||
|
block: block,
|
||||||
|
error: VerificationErrorOption::Transaction(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum VerificationErrorOption {
|
||||||
|
Transaction(TransactionVerificationError),
|
||||||
|
Block(BlockVerificationError),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TransactionVerificationError {
|
pub enum TransactionVerificationError {
|
||||||
@ -18,7 +43,6 @@ pub enum TransactionVerificationError {
|
|||||||
used: U256,
|
used: U256,
|
||||||
limit: U256
|
limit: U256
|
||||||
},
|
},
|
||||||
ExtraDataTooBig,
|
|
||||||
InvalidSignature,
|
InvalidSignature,
|
||||||
InvalidTransactionFormat,
|
InvalidTransactionFormat,
|
||||||
}
|
}
|
||||||
@ -30,8 +54,12 @@ pub enum BlockVerificationError {
|
|||||||
limit: U256,
|
limit: U256,
|
||||||
},
|
},
|
||||||
InvalidBlockFormat,
|
InvalidBlockFormat,
|
||||||
|
ExtraDataTooBig {
|
||||||
|
required: U256,
|
||||||
|
got: U256,
|
||||||
|
},
|
||||||
InvalidUnclesHash {
|
InvalidUnclesHash {
|
||||||
expected: H256,
|
required: H256,
|
||||||
got: H256,
|
got: H256,
|
||||||
},
|
},
|
||||||
TooManyUncles,
|
TooManyUncles,
|
||||||
@ -42,11 +70,18 @@ pub enum BlockVerificationError {
|
|||||||
InvalidStateRoot,
|
InvalidStateRoot,
|
||||||
InvalidGasUsed,
|
InvalidGasUsed,
|
||||||
InvalidTransactionsRoot {
|
InvalidTransactionsRoot {
|
||||||
expected: H256,
|
required: H256,
|
||||||
got: H256,
|
got: H256,
|
||||||
},
|
},
|
||||||
InvalidDifficulty,
|
InvalidDifficulty {
|
||||||
InvalidGasLimit,
|
required: U256,
|
||||||
|
got: U256,
|
||||||
|
},
|
||||||
|
InvalidGasLimit {
|
||||||
|
min: U256,
|
||||||
|
max: U256,
|
||||||
|
got: U256,
|
||||||
|
},
|
||||||
InvalidReceiptsStateRoot,
|
InvalidReceiptsStateRoot,
|
||||||
InvalidTimestamp,
|
InvalidTimestamp,
|
||||||
InvalidLogBloom,
|
InvalidLogBloom,
|
||||||
@ -93,17 +128,30 @@ pub fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_has
|
|||||||
let expected_root = ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here
|
let expected_root = ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here
|
||||||
if &expected_root != transactions_root {
|
if &expected_root != transactions_root {
|
||||||
return Err(BlockVerificationError::InvalidTransactionsRoot {
|
return Err(BlockVerificationError::InvalidTransactionsRoot {
|
||||||
expected: expected_root.clone(),
|
required: expected_root.clone(),
|
||||||
got: transactions_root.clone(),
|
got: transactions_root.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let expected_uncles = block.at(2).as_raw().sha3();
|
let expected_uncles = block.at(2).as_raw().sha3();
|
||||||
if &expected_uncles != uncles_hash {
|
if &expected_uncles != uncles_hash {
|
||||||
return Err(BlockVerificationError::InvalidUnclesHash {
|
return Err(BlockVerificationError::InvalidUnclesHash {
|
||||||
expected: expected_uncles.clone(),
|
required: expected_uncles.clone(),
|
||||||
got: uncles_hash.clone(),
|
got: uncles_hash.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn verify_block_basic(bytes: &[u8], parent: &Header, engine: &mut Engine) -> Result<(), BlockVerificationError> {
|
||||||
|
let block = BlockView::new(bytes);
|
||||||
|
let header = block.header();
|
||||||
|
try!(verify_header(&header));
|
||||||
|
try!(verify_parent(&header, parent));
|
||||||
|
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_block_unordered(block: &[u8]) -> Result<(), BlockVerificationError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user