diff --git a/src/bin/client.rs b/src/bin/client.rs index 2b003dca9..09e414476 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -1,6 +1,7 @@ extern crate ethcore_util as util; extern crate ethcore; extern crate rustc_serialize; +extern crate env_logger; use std::io::*; use std::env; @@ -12,6 +13,7 @@ use ethcore::sync::EthSync; use ethcore::ethereum; fn main() { + ::env_logger::init().ok(); let mut service = NetworkService::start().unwrap(); //TODO: replace with proper genesis and chain params. let frontier = ethereum::new_frontier(); diff --git a/src/engine.rs b/src/engine.rs index 15a92d8e8..d16f25910 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,7 +1,6 @@ use common::*; use block::Block; use spec::Spec; -use verification::VerificationError; /// 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. @@ -38,7 +37,9 @@ pub trait Engine { /// `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. // TODO: consider including State in the params. - fn verify_block(&self, _mode: VerificationMode, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + fn verify_block_final(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } /// Additional verification for transactions in blocks. // TODO: Add flags for which bits of the transaction to check. @@ -57,11 +58,3 @@ pub trait Engine { // 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 -} diff --git a/src/error.rs b/src/error.rs index 68b75ab5b..174d3c18c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,10 +17,29 @@ pub struct OutOfBounds { #[derive(Debug)] pub enum BlockError { - TooManyUncles, + TooManyUncles(OutOfBounds), UncleWrongGeneration, ExtraDataOutOfBounds(OutOfBounds), InvalidSealArity(Mismatch), + TooMuchGasUsed(OutOfBounds), + InvalidUnclesHash(Mismatch), + UncleTooOld(OutOfBounds), + UncleIsBrother(OutOfBounds), + UncleInChain(H256), + UncleParentNotInChain(H256), + InvalidStateRoot, + InvalidGasUsed, + InvalidTransactionsRoot(Mismatch), + InvalidDifficulty(Mismatch), + InvalidGasLimit(OutOfBounds), + InvalidReceiptsStateRoot, + InvalidTimestamp(OutOfBounds), + InvalidLogBloom, + InvalidBlockNonce, + InvalidParentHash(Mismatch), + InvalidNumber(OutOfBounds), + UnknownParent(H256), + UnknownUncleParent(H256), } #[derive(Debug)] diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index df4190c83..337ce9c2b 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -2,7 +2,6 @@ use common::*; use block::*; use spec::*; use engine::*; -use verification::*; /// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum /// mainnet chains in the Olympic, Frontier and Homestead eras. @@ -27,53 +26,45 @@ impl Engine for Ethash { block.state_mut().add_balance(&a, &decode(&self.spec().engine_params.get("blockReward").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 + + fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap()); + if header.difficulty < min_difficulty { + return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty }))) + } + let min_gas_limit = decode(self.spec().engine_params.get("minGasLimit").unwrap()); + if header.gas_limit < min_gas_limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas_limit, max: From::from(0), found: header.gas_limit }))); + } + let maximum_extra_data_size = self.maximum_extra_data_size(); + if header.number != From::from(0) && header.extra_data.len() > maximum_extra_data_size { + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: 0, max: maximum_extra_data_size, found: header.extra_data.len() }))); + } + // TODO: Verify seal (quick) + Ok(()) + } + + fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + // TODO: Verify seal (full) + Ok(()) + } + + fn verify_block_final(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + // Check difficulty is correct given the two timestamps. + let expected_difficulty = self.calculate_difficuty(header, parent); + if header.difficulty != expected_difficulty { + return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty }))) + } + let gas_limit_divisor = decode(self.spec().engine_params.get("gasLimitBoundDivisor").unwrap()); + let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor; + let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor; + if header.gas_limit <= min_gas || header.gas_limit >= max_gas { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas, max: max_gas, found: header.gas_limit }))); } Ok(()) } - fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), VerificationError> { Ok(()) } + fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) } } impl Ethash { @@ -120,12 +111,9 @@ fn on_close_block() { let genesis_header = engine.spec().genesis_header(); let mut db = OverlayDB::new_temp(); engine.spec().ensure_db_good(&mut db); - assert!(SecTrieDB::new(&db, &genesis_header.state_root).contains(&address_from_hex("102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c"))); - { - let s = State::from_existing(db.clone(), genesis_header.state_root.clone(), engine.account_start_nonce()); - assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64)); - } - let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]); -// let c = b.close(); + let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]); + let b = b.close(); + assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244F40000").unwrap()); } +// TODO: difficulty test diff --git a/src/sync/chain.rs b/src/sync/chain.rs index b7d550c9b..bfbf3cfb8 100644 --- a/src/sync/chain.rs +++ b/src/sync/chain.rs @@ -13,20 +13,11 @@ /// 4. Maintain sync by handling NewBlocks/NewHashes messages /// -use std::collections::{HashSet, HashMap}; -use std::cmp::{min, max}; +use util::*; use std::mem::{replace}; -use util::network::{PeerId, PacketId}; -use util::hash::{H256, FixedHash}; -use util::bytes::{Bytes}; -use util::uint::{U256}; -use util::rlp::{Rlp, UntrustedRlp, RlpStream, self}; -use util::rlp::rlptraits::{Stream, View}; -use util::rlp::rlperrors::DecoderError; -use util::sha3::Hashable; -use client::{BlockNumber, BlockChainClient, BlockStatus, QueueStatus, ImportResult}; use views::{HeaderView}; use header::{Header as BlockHeader}; +use client::{BlockNumber, BlockChainClient, BlockStatus, QueueStatus, ImportResult}; use sync::range_collection::{RangeCollection, ToUsize, FromUsize}; use sync::io::SyncIo; @@ -66,6 +57,8 @@ const NODE_DATA_PACKET: u8 = 0x0e; const GET_RECEIPTS_PACKET: u8 = 0x0f; const RECEIPTS_PACKET: u8 = 0x10; +const NETWORK_ID: U256 = ONE_U256; //TODO: get this from parent + struct Header { /// Header data data: Bytes, @@ -164,6 +157,7 @@ pub struct ChainSync { impl ChainSync { + /// Create a new instance of syncing strategy. pub fn new() -> ChainSync { ChainSync { state: SyncState::NotSynced, @@ -240,7 +234,19 @@ impl ChainSync { asking_blocks: Vec::new(), }; - trace!(target: "sync", "New peer (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer.protocol_version, peer.network_id, peer.difficulty, peer.latest, peer.genesis); + trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest, peer.genesis); + + let chain_info = io.chain().chain_info(); + if peer.genesis != chain_info.genesis_hash { + io.disable_peer(peer_id); + trace!(target: "sync", "Peer {} genesis hash not matched", peer_id); + return Ok(()); + } + if peer.network_id != NETWORK_ID { + io.disable_peer(peer_id); + trace!(target: "sync", "Peer {} network id not matched", peer_id); + return Ok(()); + } let old = self.peers.insert(peer_id.clone(), peer); if old.is_some() { @@ -380,31 +386,35 @@ impl ChainSync { let h = header_rlp.as_raw().sha3(); trace!(target: "sync", "{} -> NewBlock ({})", peer_id, h); - match io.chain().import_block(block_rlp.as_raw()) { - ImportResult::AlreadyInChain => { - trace!(target: "sync", "New block already in chain {:?}", h); - }, - ImportResult::AlreadyQueued(_) => { - trace!(target: "sync", "New block already queued {:?}", h); - }, - ImportResult::Queued(QueueStatus::Known) => { - trace!(target: "sync", "New block queued {:?}", h); - }, - ImportResult::Queued(QueueStatus::Unknown) => { - trace!(target: "sync", "New block unknown {:?}", h); - //TODO: handle too many unknown blocks - let difficulty: U256 = try!(r.val_at(1)); - let peer_difficulty = self.peers.get_mut(&peer_id).expect("ChainSync: unknown peer").difficulty; - if difficulty > peer_difficulty { - trace!(target: "sync", "Received block {:?} with no known parent. Peer needs syncing...", h); - self.sync_peer(io, peer_id, true); + let header_view = HeaderView::new(header_rlp.as_raw()); + // TODO: Decompose block and add to self.headers and self.bodies instead + if header_view.number() == From::from(self.last_imported_block + 1) { + match io.chain().import_block(block_rlp.as_raw()) { + ImportResult::AlreadyInChain => { + trace!(target: "sync", "New block already in chain {:?}", h); + }, + ImportResult::AlreadyQueued(_) => { + trace!(target: "sync", "New block already queued {:?}", h); + }, + ImportResult::Queued(QueueStatus::Known) => { + trace!(target: "sync", "New block queued {:?}", h); + }, + ImportResult::Queued(QueueStatus::Unknown) => { + trace!(target: "sync", "New block unknown {:?}", h); + //TODO: handle too many unknown blocks + let difficulty: U256 = try!(r.val_at(1)); + let peer_difficulty = self.peers.get_mut(&peer_id).expect("ChainSync: unknown peer").difficulty; + if difficulty > peer_difficulty { + trace!(target: "sync", "Received block {:?} with no known parent. Peer needs syncing...", h); + self.sync_peer(io, peer_id, true); + } + }, + ImportResult::Bad =>{ + debug!(target: "sync", "Bad new block {:?}", h); + io.disable_peer(peer_id); } - }, - ImportResult::Bad =>{ - debug!(target: "sync", "Bad new block {:?}", h); - io.disable_peer(peer_id); - } - }; + }; + } Ok(()) } @@ -448,8 +458,10 @@ impl ChainSync { /// Called by peer when it is disconnecting pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: &PeerId) { trace!(target: "sync", "== Disconnected {}", peer); - self.clear_peer_download(peer); - self.continue_sync(io); + if self.peers.contains_key(&peer) { + self.clear_peer_download(peer); + self.continue_sync(io); + } } /// Called when a new peer is connected @@ -768,7 +780,7 @@ impl ChainSync { let mut packet = RlpStream::new_list(5); let chain = io.chain().chain_info(); packet.append(&(PROTOCOL_VERSION as u32)); - packet.append(&0u32); //TODO: network id + packet.append(&NETWORK_ID); //TODO: network id packet.append(&chain.total_difficulty); packet.append(&chain.best_block_hash); packet.append(&chain.genesis_hash); diff --git a/src/sync/io.rs b/src/sync/io.rs index 54bd22f14..ed7b0fec5 100644 --- a/src/sync/io.rs +++ b/src/sync/io.rs @@ -1,10 +1,11 @@ use client::BlockChainClient; -use util::network::{HandlerIo, PeerId, PacketId, Error as NetworkError}; +use util::network::{HandlerIo, PeerId, PacketId,}; +use util::error::UtilError; pub trait SyncIo { fn disable_peer(&mut self, peer_id: &PeerId); - fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), NetworkError>; - fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec) -> Result<(), NetworkError>; + fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), UtilError>; + fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec) -> Result<(), UtilError>; fn chain<'s>(&'s mut self) -> &'s mut BlockChainClient; } @@ -27,11 +28,11 @@ impl<'s, 'h> SyncIo for NetSyncIo<'s, 'h> { self.network.disable_peer(*peer_id); } - fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), NetworkError>{ + fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), UtilError>{ self.network.respond(packet_id, data) } - fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec) -> Result<(), NetworkError>{ + fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec) -> Result<(), UtilError>{ self.network.send(peer_id, packet_id, data) } diff --git a/src/sync/tests.rs b/src/sync/tests.rs index 28e526aa9..dfcf75c8b 100644 --- a/src/sync/tests.rs +++ b/src/sync/tests.rs @@ -4,7 +4,8 @@ use util::hash::{H256, FixedHash}; use util::uint::{U256}; use util::sha3::Hashable; use util::rlp::{self, Rlp, RlpStream, View, Stream}; -use util::network::{PeerId, PacketId, Error as NetworkError}; +use util::network::{PeerId, PacketId}; +use util::error::UtilError; use client::{BlockChainClient, BlockStatus, BlockNumber, TreeRoute, BlockQueueStatus, BlockChainInfo, ImportResult, QueueStatus}; use header::Header as BlockHeader; use sync::io::SyncIo; @@ -195,7 +196,7 @@ impl<'p> SyncIo for TestIo<'p> { fn disable_peer(&mut self, _peer_id: &PeerId) { } - fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), NetworkError> { + fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), UtilError> { self.queue.push_back(TestPacket { data: data, packet_id: packet_id, @@ -204,7 +205,7 @@ impl<'p> SyncIo for TestIo<'p> { Ok(()) } - fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec) -> Result<(), NetworkError> { + fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec) -> Result<(), UtilError> { self.queue.push_back(TestPacket { data: data, packet_id: packet_id, diff --git a/src/verification.rs b/src/verification.rs index f4aa4dcb6..ae156cbfa 100644 --- a/src/verification.rs +++ b/src/verification.rs @@ -1,157 +1,139 @@ -use util::*; -use header::Header; +use common::*; use client::BlockNumber; -use engine::{Engine, VerificationMode}; -use views::BlockView; +use engine::Engine; +use blockchain::BlockChain; -#[derive(Debug)] -pub struct VerificationError { - pub block: Option, - pub error: VerificationErrorOption, -} - -impl VerificationError { - pub fn block(error: BlockVerificationError, block: Option) -> VerificationError { - VerificationError { - block: block, - error: VerificationErrorOption::Block(error), - } - } - pub fn transaction(error: TransactionVerificationError, block: Option) -> VerificationError { - VerificationError { - block: block, - error: VerificationErrorOption::Transaction(error), - } - } -} - -#[derive(Debug)] -pub enum VerificationErrorOption { - Transaction(TransactionVerificationError), - Block(BlockVerificationError), -} - -#[derive(Debug)] -pub enum TransactionVerificationError { - OutOfGasBase, - OutOfGasIntrinsic, - NotEnoughCash, - GasPriceTooLow, - BlockGasLimitReached, - FeeTooSmall, - TooMuchGasUsed { - used: U256, - limit: U256 - }, - InvalidSignature, - InvalidTransactionFormat, -} - -#[derive(Debug)] -pub enum BlockVerificationError { - TooMuchGasUsed { - used: U256, - limit: U256, - }, - InvalidBlockFormat, - ExtraDataTooBig { - required: U256, - got: U256, - }, - InvalidUnclesHash { - required: H256, - got: H256, - }, - TooManyUncles, - UncleTooOld, - UncleIsBrother, - UncleInChain, - UncleParentNotInChain, - InvalidStateRoot, - InvalidGasUsed, - InvalidTransactionsRoot { - required: H256, - got: H256, - }, - InvalidDifficulty { - required: U256, - got: U256, - }, - InvalidGasLimit { - min: U256, - max: U256, - got: U256, - }, - InvalidReceiptsStateRoot, - InvalidTimestamp, - InvalidLogBloom, - InvalidNonce, - InvalidBlockHeaderItemCount, - InvalidBlockNonce, - InvalidParentHash, - InvalidUncleParentHash, - InvalidNumber, - BlockNotFound, - UnknownParent, -} - - -pub fn verify_header(header: &Header) -> Result<(), BlockVerificationError> { +fn verify_header(header: &Header) -> Result<(), Error> { if header.number > From::from(BlockNumber::max_value()) { - return Err(BlockVerificationError::InvalidNumber) + return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: From::from(BlockNumber::max_value()), min: From::from(0), found: header.number }))) } if header.gas_used > header.gas_limit { - return Err(BlockVerificationError::TooMuchGasUsed { - used: header.gas_used, - limit: header.gas_limit, - }); + return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: header.gas_limit, min: From::from(0), found: header.gas_used }))); } Ok(()) } -pub fn verify_parent(header: &Header, parent: &Header) -> Result<(), BlockVerificationError> { +fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> { if !header.parent_hash.is_zero() && parent.hash() != header.parent_hash { - return Err(BlockVerificationError::InvalidParentHash) + return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash.clone() }))) } if header.timestamp <= parent.timestamp { - return Err(BlockVerificationError::InvalidTimestamp) + return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: BAD_U256, min: parent.timestamp + From::from(1), found: header.timestamp }))) } if header.number <= parent.number { - return Err(BlockVerificationError::InvalidNumber) + return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: From::from(BlockNumber::max_value()), min: parent.number + From::from(1), found: header.number }))); } Ok(()) } -pub fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), BlockVerificationError> { +fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> { let block = Rlp::new(block); let tx = block.at(1); - 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 { - return Err(BlockVerificationError::InvalidTransactionsRoot { - required: expected_root.clone(), - got: transactions_root.clone(), - }); + 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 { + return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() }))) } - let expected_uncles = block.at(2).as_raw().sha3(); - if &expected_uncles != uncles_hash { - return Err(BlockVerificationError::InvalidUnclesHash { - required: expected_uncles.clone(), - got: uncles_hash.clone(), - }); + let expected_uncles = &block.at(2).as_raw().sha3(); + if expected_uncles != uncles_hash { + return Err(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() }))) } Ok(()) } -pub fn verify_block_basic(bytes: &[u8], parent: &Header, engine: &mut Engine) -> Result<(), BlockVerificationError> { +pub fn verify_block_basic(bytes: &[u8], engine: &mut Engine) -> Result<(), Error> { 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)); - + try!(engine.verify_block_basic(&header, Some(bytes))); + for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { + try!(verify_header(&u)); + try!(engine.verify_block_basic(&u, None)); + } Ok(()) } -pub fn verify_block_unordered(block: &[u8]) -> Result<(), BlockVerificationError> { +pub fn verify_block_unordered(bytes: &[u8], engine: &mut Engine) -> Result<(), Error> { + let block = BlockView::new(bytes); + let header = block.header(); + try!(engine.verify_block_unordered(&header, Some(bytes))); + for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { + try!(engine.verify_block_unordered(&u, None)); + } + Ok(()) +} + +pub fn verify_block_final(bytes: &[u8], engine: &mut Engine, bc: &BlockChain) -> Result<(), Error> { + let block = BlockView::new(bytes); + let header = block.header(); + let parent = try!(bc.block_header(&header.parent_hash).ok_or::(From::from(BlockError::UnknownParent(header.parent_hash.clone())))); + try!(verify_parent(&header, &parent)); + try!(engine.verify_block_final(&header, &parent, Some(bytes))); + + let num_uncles = Rlp::new(bytes).at(2).item_count(); + if num_uncles != 0 { + if num_uncles > 2 { + return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: 0, max: 2, found: num_uncles }))); + } + + let mut excluded = HashSet::new(); + excluded.insert(header.hash()); + let mut hash = header.parent_hash.clone(); + excluded.insert(hash.clone()); + for _ in 0..6 { + match bc.block_details(&hash) { + Some(details) => { + excluded.insert(details.parent.clone()); + let b = bc.block(&hash).unwrap(); + excluded.extend(BlockView::new(&b).uncle_hashes()); + hash = details.parent; + } + None => break + } + } + + for uncle in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { + let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or::(From::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone())))); + if excluded.contains(&uncle_parent.hash()) { + return Err(From::from(BlockError::UncleInChain(uncle_parent.hash()))) + } + + // m_currentBlock.number() - uncle.number() m_cB.n - uP.n() + // 1 2 + // 2 + // 3 + // 4 + // 5 + // 6 7 + // (8 Invalid) + + let depth = if header.number > uncle.number { header.number - uncle.number } else { From::from(0) }; + if depth > From::from(6) { + return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: header.number - depth, max: header.number - From::from(1), found: uncle.number }))); + } + else if depth < From::from(1) { + return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: header.number - depth, max: header.number - From::from(1), found: uncle.number }))); + } + + // cB + // cB.p^1 1 depth, valid uncle + // cB.p^2 ---/ 2 + // cB.p^3 -----/ 3 + // cB.p^4 -------/ 4 + // cB.p^5 ---------/ 5 + // cB.p^6 -----------/ 6 + // cB.p^7 -------------/ + // cB.p^8 + let mut expected_uncle_parent = header.parent_hash.clone(); + for _ in 0..depth.as_u32() { + expected_uncle_parent = bc.block_details(&expected_uncle_parent).unwrap().parent; + } + if expected_uncle_parent != uncle_parent.hash() { + return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash()))); + } + + try!(engine.verify_block_final(&uncle, &uncle_parent, Some(bytes))); + } + } Ok(()) }