Decoding headers can fail (#8570)

* rlp::decode returns Result

* Fix journaldb to handle rlp::decode Result

* Fix ethcore to work with rlp::decode returning Result

* Light client handles rlp::decode returning Result

* Fix tests in rlp_derive

* Fix tests

* Cleanup

* cleanup

* Allow panic rather than breaking out of iterator

* Let decoding failures when reading from disk blow up

* syntax

* Fix the trivial grumbles

* Fix failing tests

* Make Account::from_rlp return Result

* Syntx, sigh

* Temp-fix for decoding failures

* Header::decode returns Result

Handle new return type throughout the code base.

* Do not continue reading from the DB when a value could not be read

* Fix tests

* Handle header decoding in light_sync

* Handling header decoding errors

* Let the DecodeError bubble up unchanged

* Remove redundant error conversion
This commit is contained in:
David
2018-05-09 12:05:56 +02:00
committed by Afri Schoedon
parent 8b0ba97cf2
commit 842b75c0e6
18 changed files with 98 additions and 53 deletions

View File

@@ -1219,8 +1219,7 @@ impl Client {
=> Some(self.chain.read().best_block_header()),
BlockId::Number(number) if number == self.chain.read().best_block_number()
=> Some(self.chain.read().best_block_header()),
_
=> self.block_header(id).map(|h| h.decode()),
_ => self.block_header(id).and_then(|h| h.decode().ok())
}
}
}
@@ -1915,7 +1914,11 @@ impl BlockChainClient for Client {
fn uncle_extra_info(&self, id: UncleId) -> Option<BTreeMap<String, String>> {
self.uncle(id)
.map(|header| self.engine.extra_info(&header.decode()))
.and_then(|h| {
h.decode().map(|dh| {
self.engine.extra_info(&dh)
}).ok()
})
}
fn pruning_info(&self) -> PruningInfo {
@@ -2033,7 +2036,8 @@ impl ReopenBlock for Client {
for h in uncles {
if !block.uncles().iter().any(|header| header.hash() == h) {
let uncle = chain.block_header_data(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed");
block.push_uncle(uncle.decode()).expect("pushing up to maximum_uncle_count;
let uncle = uncle.decode().expect("decoding failure");
block.push_uncle(uncle).expect("pushing up to maximum_uncle_count;
push_uncle is not ok only if more than maximum_uncle_count is pushed;
so all push_uncle are Ok;
qed");
@@ -2074,7 +2078,7 @@ impl PrepareOpenBlock for Client {
.into_iter()
.take(engine.maximum_uncle_count(open_block.header().number()))
.foreach(|h| {
open_block.push_uncle(h.decode()).expect("pushing maximum_uncle_count;
open_block.push_uncle(h.decode().expect("decoding failure")).expect("pushing maximum_uncle_count;
open_block was just created;
push_uncle is not ok only if more than maximum_uncle_count is pushed;
so all push_uncle are Ok;

View File

@@ -289,7 +289,7 @@ impl TestBlockChainClient {
/// Make a bad block by setting invalid extra data.
pub fn corrupt_block(&self, n: BlockNumber) {
let hash = self.block_hash(BlockId::Number(n)).unwrap();
let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode();
let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode().expect("decoding failed");
header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec());
let mut rlp = RlpStream::new_list(3);
rlp.append(&header);
@@ -301,7 +301,7 @@ impl TestBlockChainClient {
/// Make a bad block by setting invalid parent hash.
pub fn corrupt_block_parent(&self, n: BlockNumber) {
let hash = self.block_hash(BlockId::Number(n)).unwrap();
let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode();
let mut header: BlockHeader = self.block_header(BlockId::Number(n)).unwrap().decode().expect("decoding failed");
header.set_parent_hash(H256::from(42));
let mut rlp = RlpStream::new_list(3);
rlp.append(&header);
@@ -479,6 +479,7 @@ impl BlockInfo for TestBlockChainClient {
self.block_header(BlockId::Hash(self.chain_info().best_block_hash))
.expect("Best block always has header.")
.decode()
.expect("decoding failed")
}
fn block(&self, id: BlockId) -> Option<encoded::Block> {

View File

@@ -28,7 +28,7 @@ use ethereum_types::{H256, Bloom, U256, Address};
use hash::keccak;
use header::{BlockNumber, Header as FullHeader};
use heapsize::HeapSizeOf;
use rlp::{Rlp, RlpStream};
use rlp::{self, Rlp, RlpStream};
use transaction::UnverifiedTransaction;
use views::{self, BlockView, HeaderView, BodyView};
@@ -47,7 +47,9 @@ impl Header {
pub fn new(encoded: Vec<u8>) -> Self { Header(encoded) }
/// Upgrade this encoded view to a fully owned `Header` object.
pub fn decode(&self) -> FullHeader { ::rlp::decode(&self.0).expect("decoding failure") }
pub fn decode(&self) -> Result<FullHeader, rlp::DecoderError> {
rlp::decode(&self.0)
}
/// Get a borrowed header view onto the data.
#[inline]

View File

@@ -996,7 +996,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
let parent = client.block_header(::client::BlockId::Hash(*block.header().parent_hash()))
.expect("hash is from parent; parent header must exist; qed")
.decode();
.decode()?;
let parent_step = header_step(&parent, self.empty_steps_transition)?;
let current_step = self.step.load();

View File

@@ -290,6 +290,12 @@ error_chain! {
description("Unknown engine name")
display("Unknown engine name ({})", name)
}
#[doc = "RLP decoding errors"]
Decoder(err: ::rlp::DecoderError) {
description("decoding value failed")
display("decoding value failed with error: {}", err)
}
}
}
@@ -310,11 +316,11 @@ impl From<AccountsError> for Error {
fn from(err: AccountsError) -> Error {
ErrorKind::AccountProvider(err).into()
}
}
}
impl From<::rlp::DecoderError> for Error {
fn from(err: ::rlp::DecoderError) -> Error {
UtilError::from(err).into()
ErrorKind::Decoder(err).into()
}
}

View File

@@ -528,8 +528,8 @@ impl Miner {
}
/// Attempts to perform internal sealing (one that does not require work) and handles the result depending on the type of Seal.
fn seal_and_import_block_internally<C>(&self, chain: &C, block: ClosedBlock) -> bool where
C: BlockChain + SealedBlockImporter,
fn seal_and_import_block_internally<C>(&self, chain: &C, block: ClosedBlock) -> bool
where C: BlockChain + SealedBlockImporter,
{
{
let sealing = self.sealing.lock();
@@ -544,7 +544,12 @@ impl Miner {
trace!(target: "miner", "seal_block_internally: attempting internal seal.");
let parent_header = match chain.block_header(BlockId::Hash(*block.header().parent_hash())) {
Some(hdr) => hdr.decode(),
Some(h) => {
match h.decode() {
Ok(decoded_hdr) => decoded_hdr,
Err(_) => return false
}
}
None => return false,
};

View File

@@ -487,7 +487,7 @@ pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &EthEngine, ch
if always || rng.gen::<f32>() <= POW_VERIFY_RATE {
engine.verify_block_unordered(header)?;
match chain.block_header_data(header.parent_hash()) {
Some(parent) => engine.verify_block_family(header, &parent.decode()),
Some(parent) => engine.verify_block_family(header, &parent.decode()?),
None => Ok(()),
}
} else {

View File

@@ -224,7 +224,7 @@ fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &Eth
return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash())));
}
let uncle_parent = uncle_parent.decode();
let uncle_parent = uncle_parent.decode()?;
verify_parent(&uncle, &uncle_parent, engine)?;
engine.verify_block_family(&uncle, &uncle_parent)?;
verified.insert(uncle.hash());
@@ -500,10 +500,9 @@ mod tests {
// no existing tests need access to test, so having this not function
// is fine.
let client = ::client::TestBlockChainClient::default();
let parent = bc.block_header_data(header.parent_hash())
.ok_or(BlockError::UnknownParent(header.parent_hash().clone()))?
.decode();
.decode()?;
let full_params = FullFamilyParams {
block_bytes: bytes,