[Stable] Backports (#8058)
* fixed parsing ethash seals and verify_block_undordered (#8031) * fix for verify_block_basic crashing on invalid transaction rlp (#8032)
This commit is contained in:
parent
94defd7f82
commit
edbff0d34d
@ -29,7 +29,7 @@ use error::{BlockError, Error};
|
|||||||
use header::{Header, BlockNumber};
|
use header::{Header, BlockNumber};
|
||||||
use engines::{self, Engine};
|
use engines::{self, Engine};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use rlp::{self, UntrustedRlp};
|
use rlp::UntrustedRlp;
|
||||||
use machine::EthereumMachine;
|
use machine::EthereumMachine;
|
||||||
use semantic_version::SemanticVersion;
|
use semantic_version::SemanticVersion;
|
||||||
|
|
||||||
@ -41,6 +41,38 @@ const MAX_SNAPSHOT_BLOCKS: u64 = 30000;
|
|||||||
|
|
||||||
const DEFAULT_EIP649_DELAY: u64 = 3_000_000;
|
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<T: AsRef<[u8]>>(seal: &[T]) -> Result<Self, Error> {
|
||||||
|
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::<H256>()?;
|
||||||
|
let nonce = UntrustedRlp::new(seal[1].as_ref()).as_val::<H64>()?;
|
||||||
|
let seal = Seal {
|
||||||
|
mix_hash,
|
||||||
|
nonce,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(seal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Ethash params.
|
/// Ethash params.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct EthashParams {
|
pub struct EthashParams {
|
||||||
@ -177,13 +209,12 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
|||||||
|
|
||||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||||
fn extra_info(&self, header: &Header) -> BTreeMap<String, String> {
|
fn extra_info(&self, header: &Header) -> BTreeMap<String, String> {
|
||||||
if header.seal().len() == self.seal_fields() {
|
match Seal::parse_seal(header.seal()) {
|
||||||
map![
|
Ok(seal) => map![
|
||||||
"nonce".to_owned() => format!("0x{}", header.nonce().hex()),
|
"nonce".to_owned() => format!("0x{}", seal.nonce.hex()),
|
||||||
"mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())
|
"mixHash".to_owned() => format!("0x{}", seal.mix_hash.hex())
|
||||||
]
|
],
|
||||||
} else {
|
_ => BTreeMap::default()
|
||||||
BTreeMap::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,13 +300,7 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
|||||||
|
|
||||||
fn verify_block_basic(&self, header: &Header) -> Result<(), Error> {
|
fn verify_block_basic(&self, header: &Header) -> Result<(), Error> {
|
||||||
// check the seal fields.
|
// check the seal fields.
|
||||||
if header.seal().len() != self.seal_fields() {
|
let seal = Seal::parse_seal(header.seal())?;
|
||||||
return Err(From::from(BlockError::InvalidSealArity(
|
|
||||||
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
UntrustedRlp::new(&header.seal()[0]).as_val::<H256>()?;
|
|
||||||
UntrustedRlp::new(&header.seal()[1]).as_val::<H64>()?;
|
|
||||||
|
|
||||||
// TODO: consider removing these lines.
|
// TODO: consider removing these lines.
|
||||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||||
@ -285,9 +310,10 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
|||||||
|
|
||||||
let difficulty = Ethash::boundary_to_difficulty(&H256(quick_get_difficulty(
|
let difficulty = Ethash::boundary_to_difficulty(&H256(quick_get_difficulty(
|
||||||
&header.bare_hash().0,
|
&header.bare_hash().0,
|
||||||
header.nonce().low_u64(),
|
seal.nonce.low_u64(),
|
||||||
&header.mix_hash().0
|
&seal.mix_hash.0
|
||||||
)));
|
)));
|
||||||
|
|
||||||
if &difficulty < header.difficulty() {
|
if &difficulty < header.difficulty() {
|
||||||
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
|
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
|
||||||
}
|
}
|
||||||
@ -300,17 +326,20 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> {
|
fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> {
|
||||||
if header.seal().len() != self.seal_fields() {
|
let seal = Seal::parse_seal(header.seal())?;
|
||||||
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, seal.nonce.low_u64());
|
||||||
)));
|
|
||||||
}
|
|
||||||
let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, header.nonce().low_u64());
|
|
||||||
let mix = H256(result.mix_hash);
|
let mix = H256(result.mix_hash);
|
||||||
let difficulty = Ethash::boundary_to_difficulty(&H256(result.value));
|
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));
|
trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}",
|
||||||
if mix != header.mix_hash() {
|
num = header.number() as u64,
|
||||||
return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() })));
|
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() {
|
if &difficulty < header.difficulty() {
|
||||||
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: 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) {
|
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 {
|
let eras = if block_number != 0 && block_number % era_rounds == 0 {
|
||||||
block_number / era_rounds - 1
|
block_number / era_rounds - 1
|
||||||
@ -637,7 +654,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn can_do_seal_unordered_verification_fail() {
|
fn can_do_seal_unordered_verification_fail() {
|
||||||
let engine = test_spec().engine;
|
let engine = test_spec().engine;
|
||||||
let header: Header = Header::default();
|
let header = Header::default();
|
||||||
|
|
||||||
let verify_result = engine.verify_block_unordered(&header);
|
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]
|
#[test]
|
||||||
fn can_do_seal256_verification_fail() {
|
fn can_do_seal256_verification_fail() {
|
||||||
let engine = test_spec().engine;
|
let engine = test_spec().engine;
|
||||||
|
@ -28,7 +28,7 @@ use client::BlockChainClient;
|
|||||||
use engines::EthEngine;
|
use engines::EthEngine;
|
||||||
use error::{BlockError, Error};
|
use error::{BlockError, Error};
|
||||||
use header::{BlockNumber, Header};
|
use header::{BlockNumber, Header};
|
||||||
use transaction::SignedTransaction;
|
use transaction::{SignedTransaction, UnverifiedTransaction};
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
|
|
||||||
use bigint::hash::H256;
|
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)?;
|
verify_header_params(&u, engine, false)?;
|
||||||
engine.verify_block_basic(&u)?;
|
engine.verify_block_basic(&u)?;
|
||||||
}
|
}
|
||||||
// Verify transactions.
|
|
||||||
// TODO: either use transaction views or cache the decoded transactions.
|
for t in UntrustedRlp::new(bytes).at(1)?.iter().map(|rlp| rlp.as_val::<UnverifiedTransaction>()) {
|
||||||
let v = BlockView::new(bytes);
|
engine.verify_transaction_basic(&t?, &header)?;
|
||||||
for t in v.transactions() {
|
|
||||||
engine.verify_transaction_basic(&t, &header)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -355,6 +353,7 @@ mod tests {
|
|||||||
use types::log_entry::{LogEntry, LocalizedLogEntry};
|
use types::log_entry::{LogEntry, LocalizedLogEntry};
|
||||||
use time::get_time;
|
use time::get_time;
|
||||||
use encoded;
|
use encoded;
|
||||||
|
use rlp;
|
||||||
|
|
||||||
fn check_ok(result: Result<(), Error>) {
|
fn check_ok(result: Result<(), Error>) {
|
||||||
result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e));
|
result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e));
|
||||||
@ -508,6 +507,27 @@ mod tests {
|
|||||||
Ok(())
|
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::<Vec<u8>, _>(&invalid_transactions);
|
||||||
|
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
|
||||||
|
rlp.out()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(basic_test(&block, engine).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_block() {
|
fn test_verify_block() {
|
||||||
use rlp::RlpStream;
|
use rlp::RlpStream;
|
||||||
|
Loading…
Reference in New Issue
Block a user