Merge branch 'network' into verification
This commit is contained in:
commit
5f4cd7f197
@ -1,6 +1,7 @@
|
|||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
extern crate env_logger;
|
||||||
|
|
||||||
use std::io::*;
|
use std::io::*;
|
||||||
use std::env;
|
use std::env;
|
||||||
@ -12,6 +13,7 @@ use ethcore::sync::EthSync;
|
|||||||
use ethcore::ethereum;
|
use ethcore::ethereum;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
::env_logger::init().ok();
|
||||||
let mut service = NetworkService::start().unwrap();
|
let mut service = NetworkService::start().unwrap();
|
||||||
//TODO: replace with proper genesis and chain params.
|
//TODO: replace with proper genesis and chain params.
|
||||||
let frontier = ethereum::new_frontier();
|
let frontier = ethereum::new_frontier();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
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.
|
||||||
@ -38,7 +37,9 @@ 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, _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.
|
/// 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.
|
||||||
@ -57,11 +58,3 @@ 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
|
|
||||||
}
|
|
||||||
|
21
src/error.rs
21
src/error.rs
@ -17,10 +17,29 @@ pub struct OutOfBounds<T: fmt::Debug> {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum BlockError {
|
pub enum BlockError {
|
||||||
TooManyUncles,
|
TooManyUncles(OutOfBounds<usize>),
|
||||||
UncleWrongGeneration,
|
UncleWrongGeneration,
|
||||||
ExtraDataOutOfBounds(OutOfBounds<usize>),
|
ExtraDataOutOfBounds(OutOfBounds<usize>),
|
||||||
InvalidSealArity(Mismatch<usize>),
|
InvalidSealArity(Mismatch<usize>),
|
||||||
|
TooMuchGasUsed(OutOfBounds<U256>),
|
||||||
|
InvalidUnclesHash(Mismatch<H256>),
|
||||||
|
UncleTooOld(OutOfBounds<U256>),
|
||||||
|
UncleIsBrother(OutOfBounds<U256>),
|
||||||
|
UncleInChain(H256),
|
||||||
|
UncleParentNotInChain(H256),
|
||||||
|
InvalidStateRoot,
|
||||||
|
InvalidGasUsed,
|
||||||
|
InvalidTransactionsRoot(Mismatch<H256>),
|
||||||
|
InvalidDifficulty(Mismatch<U256>),
|
||||||
|
InvalidGasLimit(OutOfBounds<U256>),
|
||||||
|
InvalidReceiptsStateRoot,
|
||||||
|
InvalidTimestamp(OutOfBounds<U256>),
|
||||||
|
InvalidLogBloom,
|
||||||
|
InvalidBlockNonce,
|
||||||
|
InvalidParentHash(Mismatch<H256>),
|
||||||
|
InvalidNumber(OutOfBounds<U256>),
|
||||||
|
UnknownParent(H256),
|
||||||
|
UnknownUncleParent(H256),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -2,7 +2,6 @@ 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.
|
||||||
@ -27,53 +26,45 @@ impl Engine for Ethash {
|
|||||||
block.state_mut().add_balance(&a, &decode(&self.spec().engine_params.get("blockReward").unwrap()));
|
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 {
|
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
if header.difficulty < min_difficulty {
|
if header.difficulty < min_difficulty {
|
||||||
return Err(VerificationError::block(
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty })))
|
||||||
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());
|
let min_gas_limit = decode(self.spec().engine_params.get("minGasLimit").unwrap());
|
||||||
if header.gas_limit < min_gas_limit {
|
if header.gas_limit < min_gas_limit {
|
||||||
return Err(VerificationError::block(
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas_limit, max: From::from(0), found: header.gas_limit })));
|
||||||
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 = self.maximum_extra_data_size();
|
||||||
let maximum_extra_data_size: U256 = From::from(self.maximum_extra_data_size());
|
if header.number != From::from(0) && header.extra_data.len() > maximum_extra_data_size {
|
||||||
if header.number != From::from(0) && len > maximum_extra_data_size {
|
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: 0, max: maximum_extra_data_size, found: header.extra_data.len() })));
|
||||||
return Err(VerificationError::block(
|
|
||||||
BlockVerificationError::ExtraDataTooBig { required: maximum_extra_data_size, got: len },
|
|
||||||
block.map(|b| b.to_vec())));
|
|
||||||
}
|
}
|
||||||
match parent {
|
// TODO: Verify seal (quick)
|
||||||
Some(p) => {
|
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.
|
// Check difficulty is correct given the two timestamps.
|
||||||
let expected_difficulty = self.calculate_difficuty(header, p);
|
let expected_difficulty = self.calculate_difficuty(header, parent);
|
||||||
if header.difficulty != expected_difficulty {
|
if header.difficulty != expected_difficulty {
|
||||||
return Err(VerificationError::block(
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty })))
|
||||||
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 gas_limit_divisor = decode(self.spec().engine_params.get("gasLimitBoundDivisor").unwrap());
|
||||||
let min_gas = p.gas_limit - p.gas_limit / gas_limit_divisor;
|
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
|
||||||
let max_gas = p.gas_limit + p.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 {
|
if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
|
||||||
return Err(VerificationError::block(
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas, max: max_gas, found: header.gas_limit })));
|
||||||
BlockVerificationError::InvalidGasLimit { min: min_gas_limit, max: max_gas, got: header.gas_limit },
|
|
||||||
block.map(|b| b.to_vec())));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => ()
|
|
||||||
}
|
|
||||||
// TODO: Verify seal
|
|
||||||
}
|
}
|
||||||
Ok(())
|
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 {
|
impl Ethash {
|
||||||
@ -120,12 +111,9 @@ fn on_close_block() {
|
|||||||
let genesis_header = engine.spec().genesis_header();
|
let genesis_header = engine.spec().genesis_header();
|
||||||
let mut db = OverlayDB::new_temp();
|
let mut db = OverlayDB::new_temp();
|
||||||
engine.spec().ensure_db_good(&mut db);
|
engine.spec().ensure_db_good(&mut db);
|
||||||
assert!(SecTrieDB::new(&db, &genesis_header.state_root).contains(&address_from_hex("102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c")));
|
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]);
|
||||||
{
|
let b = b.close();
|
||||||
let s = State::from_existing(db.clone(), genesis_header.state_root.clone(), engine.account_start_nonce());
|
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244F40000").unwrap());
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: difficulty test
|
||||||
|
@ -13,20 +13,11 @@
|
|||||||
/// 4. Maintain sync by handling NewBlocks/NewHashes messages
|
/// 4. Maintain sync by handling NewBlocks/NewHashes messages
|
||||||
///
|
///
|
||||||
|
|
||||||
use std::collections::{HashSet, HashMap};
|
use util::*;
|
||||||
use std::cmp::{min, max};
|
|
||||||
use std::mem::{replace};
|
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 views::{HeaderView};
|
||||||
use header::{Header as BlockHeader};
|
use header::{Header as BlockHeader};
|
||||||
|
use client::{BlockNumber, BlockChainClient, BlockStatus, QueueStatus, ImportResult};
|
||||||
use sync::range_collection::{RangeCollection, ToUsize, FromUsize};
|
use sync::range_collection::{RangeCollection, ToUsize, FromUsize};
|
||||||
use sync::io::SyncIo;
|
use sync::io::SyncIo;
|
||||||
|
|
||||||
@ -66,6 +57,8 @@ const NODE_DATA_PACKET: u8 = 0x0e;
|
|||||||
const GET_RECEIPTS_PACKET: u8 = 0x0f;
|
const GET_RECEIPTS_PACKET: u8 = 0x0f;
|
||||||
const RECEIPTS_PACKET: u8 = 0x10;
|
const RECEIPTS_PACKET: u8 = 0x10;
|
||||||
|
|
||||||
|
const NETWORK_ID: U256 = ONE_U256; //TODO: get this from parent
|
||||||
|
|
||||||
struct Header {
|
struct Header {
|
||||||
/// Header data
|
/// Header data
|
||||||
data: Bytes,
|
data: Bytes,
|
||||||
@ -164,6 +157,7 @@ pub struct ChainSync {
|
|||||||
|
|
||||||
|
|
||||||
impl ChainSync {
|
impl ChainSync {
|
||||||
|
/// Create a new instance of syncing strategy.
|
||||||
pub fn new() -> ChainSync {
|
pub fn new() -> ChainSync {
|
||||||
ChainSync {
|
ChainSync {
|
||||||
state: SyncState::NotSynced,
|
state: SyncState::NotSynced,
|
||||||
@ -240,7 +234,19 @@ impl ChainSync {
|
|||||||
asking_blocks: Vec::new(),
|
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);
|
let old = self.peers.insert(peer_id.clone(), peer);
|
||||||
if old.is_some() {
|
if old.is_some() {
|
||||||
@ -380,6 +386,9 @@ impl ChainSync {
|
|||||||
let h = header_rlp.as_raw().sha3();
|
let h = header_rlp.as_raw().sha3();
|
||||||
|
|
||||||
trace!(target: "sync", "{} -> NewBlock ({})", peer_id, h);
|
trace!(target: "sync", "{} -> NewBlock ({})", peer_id, h);
|
||||||
|
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()) {
|
match io.chain().import_block(block_rlp.as_raw()) {
|
||||||
ImportResult::AlreadyInChain => {
|
ImportResult::AlreadyInChain => {
|
||||||
trace!(target: "sync", "New block already in chain {:?}", h);
|
trace!(target: "sync", "New block already in chain {:?}", h);
|
||||||
@ -405,6 +414,7 @@ impl ChainSync {
|
|||||||
io.disable_peer(peer_id);
|
io.disable_peer(peer_id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,9 +458,11 @@ impl ChainSync {
|
|||||||
/// Called by peer when it is disconnecting
|
/// Called by peer when it is disconnecting
|
||||||
pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: &PeerId) {
|
pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: &PeerId) {
|
||||||
trace!(target: "sync", "== Disconnected {}", peer);
|
trace!(target: "sync", "== Disconnected {}", peer);
|
||||||
|
if self.peers.contains_key(&peer) {
|
||||||
self.clear_peer_download(peer);
|
self.clear_peer_download(peer);
|
||||||
self.continue_sync(io);
|
self.continue_sync(io);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when a new peer is connected
|
/// Called when a new peer is connected
|
||||||
pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: &PeerId) {
|
pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: &PeerId) {
|
||||||
@ -768,7 +780,7 @@ impl ChainSync {
|
|||||||
let mut packet = RlpStream::new_list(5);
|
let mut packet = RlpStream::new_list(5);
|
||||||
let chain = io.chain().chain_info();
|
let chain = io.chain().chain_info();
|
||||||
packet.append(&(PROTOCOL_VERSION as u32));
|
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.total_difficulty);
|
||||||
packet.append(&chain.best_block_hash);
|
packet.append(&chain.best_block_hash);
|
||||||
packet.append(&chain.genesis_hash);
|
packet.append(&chain.genesis_hash);
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use client::BlockChainClient;
|
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 {
|
pub trait SyncIo {
|
||||||
fn disable_peer(&mut self, peer_id: &PeerId);
|
fn disable_peer(&mut self, peer_id: &PeerId);
|
||||||
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), NetworkError>;
|
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError>;
|
||||||
fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), NetworkError>;
|
fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError>;
|
||||||
fn chain<'s>(&'s mut self) -> &'s mut BlockChainClient;
|
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);
|
self.network.disable_peer(*peer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), NetworkError>{
|
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError>{
|
||||||
self.network.respond(packet_id, data)
|
self.network.respond(packet_id, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), NetworkError>{
|
fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError>{
|
||||||
self.network.send(peer_id, packet_id, data)
|
self.network.send(peer_id, packet_id, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@ use util::hash::{H256, FixedHash};
|
|||||||
use util::uint::{U256};
|
use util::uint::{U256};
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use util::rlp::{self, Rlp, RlpStream, View, Stream};
|
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 client::{BlockChainClient, BlockStatus, BlockNumber, TreeRoute, BlockQueueStatus, BlockChainInfo, ImportResult, QueueStatus};
|
||||||
use header::Header as BlockHeader;
|
use header::Header as BlockHeader;
|
||||||
use sync::io::SyncIo;
|
use sync::io::SyncIo;
|
||||||
@ -195,7 +196,7 @@ impl<'p> SyncIo for TestIo<'p> {
|
|||||||
fn disable_peer(&mut self, _peer_id: &PeerId) {
|
fn disable_peer(&mut self, _peer_id: &PeerId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), NetworkError> {
|
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
||||||
self.queue.push_back(TestPacket {
|
self.queue.push_back(TestPacket {
|
||||||
data: data,
|
data: data,
|
||||||
packet_id: packet_id,
|
packet_id: packet_id,
|
||||||
@ -204,7 +205,7 @@ impl<'p> SyncIo for TestIo<'p> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), NetworkError> {
|
fn send(&mut self, peer_id: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
||||||
self.queue.push_back(TestPacket {
|
self.queue.push_back(TestPacket {
|
||||||
data: data,
|
data: data,
|
||||||
packet_id: packet_id,
|
packet_id: packet_id,
|
||||||
|
@ -1,157 +1,139 @@
|
|||||||
use util::*;
|
use common::*;
|
||||||
use header::Header;
|
|
||||||
use client::BlockNumber;
|
use client::BlockNumber;
|
||||||
use engine::{Engine, VerificationMode};
|
use engine::Engine;
|
||||||
use views::BlockView;
|
use blockchain::BlockChain;
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn verify_header(header: &Header) -> Result<(), Error> {
|
||||||
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)]
|
|
||||||
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> {
|
|
||||||
if header.number > From::from(BlockNumber::max_value()) {
|
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 {
|
if header.gas_used > header.gas_limit {
|
||||||
return Err(BlockVerificationError::TooMuchGasUsed {
|
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: header.gas_limit, min: From::from(0), found: header.gas_used })));
|
||||||
used: header.gas_used,
|
|
||||||
limit: header.gas_limit,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Ok(())
|
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 {
|
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 {
|
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 {
|
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(())
|
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 block = Rlp::new(block);
|
||||||
let tx = block.at(1);
|
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
|
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(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() })))
|
||||||
required: expected_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(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() })))
|
||||||
required: expected_uncles.clone(),
|
|
||||||
got: uncles_hash.clone(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Ok(())
|
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 block = BlockView::new(bytes);
|
||||||
let header = block.header();
|
let header = block.header();
|
||||||
try!(verify_header(&header));
|
try!(verify_header(&header));
|
||||||
try!(verify_parent(&header, parent));
|
|
||||||
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
|
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::<Header>()) {
|
||||||
|
try!(verify_header(&u));
|
||||||
|
try!(engine.verify_block_basic(&u, None));
|
||||||
|
}
|
||||||
Ok(())
|
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::<Header>()) {
|
||||||
|
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::<Error>(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::<Header>()) {
|
||||||
|
let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or::<Error>(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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user