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