diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 52b4fc108c1..37059f58514 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -29,7 +29,7 @@ use error::{BlockError, Error}; use header::{Header, BlockNumber}; use engines::{self, Engine}; use ethjson; -use rlp::{self, UntrustedRlp}; +use rlp::UntrustedRlp; use machine::EthereumMachine; use semantic_version::SemanticVersion; @@ -41,6 +41,38 @@ const MAX_SNAPSHOT_BLOCKS: u64 = 30000; const DEFAULT_EIP649_DELAY: u64 = 3_000_000; +/// Ethash specific seal +#[derive(Debug, PartialEq)] +pub struct Seal { + /// Ethash seal mix_hash + pub mix_hash: H256, + /// Ethash seal nonce + pub nonce: H64, +} + +impl Seal { + /// Tries to parse rlp as ethash seal. + pub fn parse_seal>(seal: &[T]) -> Result { + if seal.len() != 2 { + return Err(BlockError::InvalidSealArity( + Mismatch { + expected: 2, + found: seal.len() + } + ).into()); + } + + let mix_hash = UntrustedRlp::new(seal[0].as_ref()).as_val::()?; + let nonce = UntrustedRlp::new(seal[1].as_ref()).as_val::()?; + let seal = Seal { + mix_hash, + nonce, + }; + + Ok(seal) + } +} + /// Ethash params. #[derive(Debug, PartialEq)] pub struct EthashParams { @@ -177,13 +209,12 @@ impl Engine for Arc { /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, header: &Header) -> BTreeMap { - if header.seal().len() == self.seal_fields() { - map![ - "nonce".to_owned() => format!("0x{}", header.nonce().hex()), - "mixHash".to_owned() => format!("0x{}", header.mix_hash().hex()) - ] - } else { - BTreeMap::default() + match Seal::parse_seal(header.seal()) { + Ok(seal) => map![ + "nonce".to_owned() => format!("0x{}", seal.nonce.hex()), + "mixHash".to_owned() => format!("0x{}", seal.mix_hash.hex()) + ], + _ => BTreeMap::default() } } @@ -269,13 +300,7 @@ impl Engine for Arc { fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { // check the seal fields. - if header.seal().len() != self.seal_fields() { - return Err(From::from(BlockError::InvalidSealArity( - Mismatch { expected: self.seal_fields(), found: header.seal().len() } - ))); - } - UntrustedRlp::new(&header.seal()[0]).as_val::()?; - UntrustedRlp::new(&header.seal()[1]).as_val::()?; + let seal = Seal::parse_seal(header.seal())?; // TODO: consider removing these lines. let min_difficulty = self.ethash_params.minimum_difficulty; @@ -285,9 +310,10 @@ impl Engine for Arc { let difficulty = Ethash::boundary_to_difficulty(&H256(quick_get_difficulty( &header.bare_hash().0, - header.nonce().low_u64(), - &header.mix_hash().0 + seal.nonce.low_u64(), + &seal.mix_hash.0 ))); + if &difficulty < header.difficulty() { return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); } @@ -300,17 +326,20 @@ impl Engine for Arc { } fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> { - if header.seal().len() != self.seal_fields() { - return Err(From::from(BlockError::InvalidSealArity( - Mismatch { expected: self.seal_fields(), found: header.seal().len() } - ))); - } - let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, header.nonce().low_u64()); + let seal = Seal::parse_seal(header.seal())?; + + let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, seal.nonce.low_u64()); let mix = H256(result.mix_hash); let difficulty = Ethash::boundary_to_difficulty(&H256(result.value)); - trace!(target: "miner", "num: {}, seed: {}, h: {}, non: {}, mix: {}, res: {}" , header.number() as u64, H256(slow_hash_block_number(header.number() as u64)), header.bare_hash(), header.nonce().low_u64(), H256(result.mix_hash), H256(result.value)); - if mix != header.mix_hash() { - return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() }))); + trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}", + num = header.number() as u64, + seed = H256(slow_hash_block_number(header.number() as u64)), + h = header.bare_hash(), + non = seal.nonce.low_u64(), + mix = H256(result.mix_hash), + res = H256(result.value)); + if mix != seal.mix_hash { + return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: seal.mix_hash }))); } if &difficulty < header.difficulty() { return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); @@ -441,18 +470,6 @@ impl Ethash { } } -impl Header { - /// Get the nonce field of the header. - pub fn nonce(&self) -> H64 { - rlp::decode(&self.seal()[1]) - } - - /// Get the mix hash field of the header. - pub fn mix_hash(&self) -> H256 { - rlp::decode(&self.seal()[0]) - } -} - fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u64) -> (u64, U256) { let eras = if block_number != 0 && block_number % era_rounds == 0 { block_number / era_rounds - 1 @@ -637,7 +654,7 @@ mod tests { #[test] fn can_do_seal_unordered_verification_fail() { let engine = test_spec().engine; - let header: Header = Header::default(); + let header = Header::default(); let verify_result = engine.verify_block_unordered(&header); @@ -648,6 +665,17 @@ mod tests { } } + #[test] + fn can_do_seal_unordered_verification_fail2() { + let engine = test_spec().engine; + let mut header = Header::default(); + header.set_seal(vec![vec![], vec![]]); + + let verify_result = engine.verify_block_unordered(&header); + // rlp error, shouldn't panic + assert!(verify_result.is_err()); + } + #[test] fn can_do_seal256_verification_fail() { let engine = test_spec().engine; diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 7ac943d0ee1..89a4e25fd95 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -28,7 +28,7 @@ use client::BlockChainClient; use engines::EthEngine; use error::{BlockError, Error}; use header::{BlockNumber, Header}; -use transaction::SignedTransaction; +use transaction::{SignedTransaction, UnverifiedTransaction}; use views::BlockView; use bigint::hash::H256; @@ -69,11 +69,9 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &EthEngine) -> verify_header_params(&u, engine, false)?; engine.verify_block_basic(&u)?; } - // Verify transactions. - // TODO: either use transaction views or cache the decoded transactions. - let v = BlockView::new(bytes); - for t in v.transactions() { - engine.verify_transaction_basic(&t, &header)?; + + for t in UntrustedRlp::new(bytes).at(1)?.iter().map(|rlp| rlp.as_val::()) { + engine.verify_transaction_basic(&t?, &header)?; } Ok(()) } @@ -355,6 +353,7 @@ mod tests { use types::log_entry::{LogEntry, LocalizedLogEntry}; use time::get_time; use encoded; + use rlp; fn check_ok(result: Result<(), Error>) { result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); @@ -508,6 +507,27 @@ mod tests { Ok(()) } + #[test] + fn test_verify_block_basic_with_invalid_transactions() { + let spec = Spec::new_test(); + let engine = &*spec.engine; + + let block = { + let mut rlp = rlp::RlpStream::new_list(3); + let mut header = Header::default(); + // that's an invalid transaction list rlp + let invalid_transactions = vec![vec![0u8]]; + header.set_transactions_root(ordered_trie_root(invalid_transactions.clone())); + header.set_gas_limit(engine.params().min_gas_limit); + rlp.append(&header); + rlp.append_list::, _>(&invalid_transactions); + rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); + rlp.out() + }; + + assert!(basic_test(&block, engine).is_err()); + } + #[test] fn test_verify_block() { use rlp::RlpStream;