Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Rlp decode returns Result #8527

Merged
merged 17 commits into from
May 8, 2018
Merged
53 changes: 41 additions & 12 deletions ethcore/light/src/client/header_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,15 @@ impl HeaderChain {
let decoded_header = spec.genesis_header();

let chain = if let Some(current) = db.get(col, CURRENT_KEY)? {
let curr : BestAndLatest = ::rlp::decode(&current);
let curr : BestAndLatest = ::rlp::decode(&current)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be expect, cause we're reading from the db :)


let mut cur_number = curr.latest_num;
let mut candidates = BTreeMap::new();

// load all era entries, referenced headers within them,
// and live epoch proofs.
while let Some(entry) = db.get(col, era_key(cur_number).as_bytes())? {
let entry: Entry = ::rlp::decode(&entry);
let entry: Entry = ::rlp::decode(&entry)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be expect, cause we're reading from the db :)

trace!(target: "chain", "loaded header chain entry for era {} with {} candidates",
cur_number, entry.candidates.len());

Expand Down Expand Up @@ -524,7 +524,16 @@ impl HeaderChain {
None
}
Ok(None) => panic!("stored candidates always have corresponding headers; qed"),
Ok(Some(header)) => Some((epoch_transition, ::rlp::decode(&header))),
Ok(Some(header)) => {
match ::rlp::decode(&header) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this value was fetched from the database. It should never be invalid, therefore we should use expect here

Ok(decoded_header) => Some((epoch_transition, decoded_header)),
Err(e) => {
warn!(target: "chain", "Error decoding header: {}\n", e);
None
}
}

},
};
}
}
Expand Down Expand Up @@ -591,7 +600,7 @@ impl HeaderChain {
in an inconsistent state", h_num);
ErrorKind::Database(msg.into())
})?;
::rlp::decode(&bytes)
::rlp::decode(&bytes)?
};

let total_difficulty = entry.candidates.iter()
Expand All @@ -604,9 +613,9 @@ impl HeaderChain {
.total_difficulty;

break Ok(Some(SpecHardcodedSync {
header: header,
total_difficulty: total_difficulty,
chts: chts,
header,
total_difficulty,
chts,
}));
},
None => {
Expand Down Expand Up @@ -742,7 +751,17 @@ impl HeaderChain {
/// so including it within a CHT would be redundant.
pub fn cht_root(&self, n: usize) -> Option<H256> {
match self.db.get(self.col, cht_key(n as u64).as_bytes()) {
Ok(val) => val.map(|x| ::rlp::decode(&x)),
Ok(db_fetch) => {
db_fetch.map_or(None, |bytes| {
match ::rlp::decode(&bytes) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Ok(h256) => Some(h256),
Err(e) => {
warn!(target: "chain", "Error decoding data from database: {}", e);
None
}
}
})
},
Err(e) => {
warn!(target: "chain", "Error reading from database: {}", e);
None
Expand Down Expand Up @@ -793,7 +812,17 @@ impl HeaderChain {
pub fn pending_transition(&self, hash: H256) -> Option<PendingEpochTransition> {
let key = pending_transition_key(hash);
match self.db.get(self.col, &*key) {
Ok(val) => val.map(|x| ::rlp::decode(&x)),
Ok(db_fetch) => {
db_fetch.map_or(None, |bytes| {
match ::rlp::decode(&bytes) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

Ok(pending_transition) => Some(pending_transition),
Err(e) => {
warn!(target: "chain", "Error decoding data from database: {}", e);
None
}
}
})
},
Err(e) => {
warn!(target: "chain", "Error reading from database: {}", e);
None
Expand Down Expand Up @@ -1192,7 +1221,7 @@ mod tests {

let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600))));

let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap();
let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).expect("failed to instantiate a new HeaderChain");

let mut parent_hash = genesis_header.hash();
let mut rolling_timestamp = genesis_header.timestamp();
Expand All @@ -1211,14 +1240,14 @@ mod tests {
parent_hash = header.hash();

let mut tx = db.transaction();
let pending = chain.insert(&mut tx, header, None).unwrap();
let pending = chain.insert(&mut tx, header, None).expect("failed inserting a transaction");
db.write(tx).unwrap();
chain.apply_pending(pending);

rolling_timestamp += 10;
}

let hardcoded_sync = chain.read_hardcoded_sync().unwrap().unwrap();
let hardcoded_sync = chain.read_hardcoded_sync().expect("failed reading hardcoded sync").expect("failed unwrapping hardcoded sync");
assert_eq!(hardcoded_sync.chts.len(), 3);
assert_eq!(hardcoded_sync.total_difficulty, total_difficulty);
let decoded: Header = hardcoded_sync.header.decode();
Expand Down
2 changes: 1 addition & 1 deletion ethcore/light/src/net/request_credits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ mod tests {
let costs = CostTable::default();
let serialized = ::rlp::encode(&costs);

let new_costs: CostTable = ::rlp::decode(&*serialized);
let new_costs: CostTable = ::rlp::decode(&*serialized).unwrap();

assert_eq!(costs, new_costs);
}
Expand Down
2 changes: 1 addition & 1 deletion ethcore/light/src/types/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1642,7 +1642,7 @@ mod tests {
{
// check as single value.
let bytes = ::rlp::encode(&val);
let new_val: T = ::rlp::decode(&bytes);
let new_val: T = ::rlp::decode(&bytes).unwrap();
assert_eq!(val, new_val);

// check as list containing single value.
Expand Down
5 changes: 4 additions & 1 deletion ethcore/src/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,10 @@ impl<'a> Iterator for EpochTransitionIter<'a> {
return None
}

let transitions: EpochTransitions = ::rlp::decode(&val[..]);
let transitions: EpochTransitions = match ::rlp::decode(&val[..]) {
Ok(decoded) => decoded,
Err(_) => return None
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't break out of the iterator with invalid RLP. Since we wrote the EpochTransitions to the db in the first place then either the db is corrupted or the data structure has changed, so should panic.


// if there are multiple candidates, at most one will be on the
// canon chain.
Expand Down
11 changes: 9 additions & 2 deletions ethcore/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,18 @@ impl Writable for DBTransaction {
}

impl<KVDB: KeyValueDB + ?Sized> Readable for KVDB {
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T> where T: rlp::Decodable, R: Deref<Target = [u8]> {
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T>
where T: rlp::Decodable, R: Deref<Target = [u8]> {
let result = self.get(col, &key.key());

match result {
Ok(option) => option.map(|v| rlp::decode(&v)),
Ok(option) => option.map(|v| match rlp::decode(&v){
Ok(decoded_rlp) => decoded_rlp,
//REVIEW: Should we panic here?
Err(decode_err) => {
panic!("decoding db value failed, key={:?}, err={}", &key.key() as &[u8], decode_err)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@debris the situation here is similar to the above, db value read from disc, so panicking is ok. Q: do you prefer me to shorten this up with a rlp::decode(&v).expect("decoding db value failed") or is it useful to have the additional info about which variant of the decode error and the key?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it could be shortened with:

result.and_then(|option| match option {
  None => Ok(None),
  Some(v) => rlp::decode(&v).map(Some)
}).expect("decoding db value failed)

}
}),
Err(err) => {
panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
}
Expand Down
4 changes: 2 additions & 2 deletions ethcore/src/encoded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ 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) }
pub fn decode(&self) -> FullHeader { ::rlp::decode(&self.0).unwrap_or(FullHeader::default()) } // REVIEW: is the default ok here? Useful? The real fix would be to make `decode` return a `Result` as well.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should definitely return Result. It it requires too many changes, let's replace it with .expect for now and let's create another issues to track this

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/// Get a borrowed header view onto the data.
#[inline]
Expand Down Expand Up @@ -205,7 +205,7 @@ impl Block {
pub fn header_view(&self) -> HeaderView { self.view().header_view() }

/// Decode to a full block.
pub fn decode(&self) -> FullBlock { ::rlp::decode(&self.0) }
pub fn decode(&self) -> FullBlock { ::rlp::decode(&self.0).unwrap_or(FullBlock::default()) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Returning default is not a good pattern and may lead too undefined behaviours

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/// Decode the header.
pub fn decode_header(&self) -> FullHeader { self.view().rlp().val_at(0) }
Expand Down
6 changes: 4 additions & 2 deletions ethcore/src/engines/tendermint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,10 @@ impl <F> super::EpochVerifier<EthereumMachine> for EpochVerifier<F>
}

fn check_finality_proof(&self, proof: &[u8]) -> Option<Vec<H256>> {
let header: Header = ::rlp::decode(proof);
self.verify_light(&header).ok().map(|_| vec![header.hash()])
match ::rlp::decode(proof) {
Ok(header) => self.verify_light(&header).ok().map(|_| vec![header.hash()]),
Err(_) => None // REVIEW: log perhaps? Not sure what the policy is.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good question. According to interface documentation:

	/// Returns `Some(hashes)` if the proof proves finality of these hashes.
	/// Returns `None` if the proof doesn't prove anything.

it should be ok to return None in case of invalid proof. Can you confirm @rphmeier ?

}
}
}

Expand Down
4 changes: 2 additions & 2 deletions ethcore/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ mod tests {
let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap();
let nonce_decoded = "ab4e252a7e8c2a23".from_hex().unwrap();

let header: Header = rlp::decode(&header_rlp);
let header: Header = rlp::decode(&header_rlp).expect("error decoding header");
let seal_fields = header.seal.clone();
assert_eq!(seal_fields.len(), 2);
assert_eq!(seal_fields[0], mix_hash);
Expand All @@ -415,7 +415,7 @@ mod tests {
// that's rlp of block header created with ethash engine.
let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap();

let header: Header = rlp::decode(&header_rlp);
let header: Header = rlp::decode(&header_rlp).expect("error decoding header");
let encoded_header = rlp::encode(&header).into_vec();

assert_eq!(header_rlp, encoded_header);
Expand Down
6 changes: 3 additions & 3 deletions ethcore/src/snapshot/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ mod tests {
};

let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp).unwrap(), account);

let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value(), usize::max_value()).unwrap();
let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap();
Expand All @@ -261,7 +261,7 @@ mod tests {
};

let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp).unwrap(), account);

let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value(), usize::max_value()).unwrap();
let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap();
Expand All @@ -286,7 +286,7 @@ mod tests {
};

let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp).unwrap(), account);

let fat_rlps = to_fat_rlps(&keccak(addr), &account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), 500, 1000).unwrap();
let mut root = KECCAK_NULL_RLP;
Expand Down
6 changes: 3 additions & 3 deletions ethcore/src/snapshot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
// account_key here is the address' hash.
for item in account_trie.iter()? {
let (account_key, account_data) = item?;
let account = ::rlp::decode(&*account_data);
let account = ::rlp::decode(&*account_data)?;
let account_key_hash = H256::from_slice(&account_key);

let account_db = AccountDB::from_hash(db, account_key_hash);
Expand Down Expand Up @@ -467,10 +467,10 @@ fn rebuild_accounts(
*out = (hash, thin_rlp);
}
if let Some(&(ref hash, ref rlp)) = out_chunk.iter().last() {
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp).storage_root);
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp)?.storage_root);
}
if let Some(&(ref hash, ref rlp)) = out_chunk.iter().next() {
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp).storage_root);
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp)?.storage_root);
}
Ok(status)
}
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/snapshot/tests/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl StateProducer {

// sweep once to alter storage tries.
for &mut (ref mut address_hash, ref mut account_data) in &mut accounts_to_modify {
let mut account: BasicAccount = ::rlp::decode(&*account_data);
let mut account: BasicAccount = ::rlp::decode(&*account_data).expect("error decoding basic account");
let acct_db = AccountDBMut::from_hash(db, *address_hash);
fill_storage(acct_db, &mut account.storage_root, &mut self.storage_seed);
*account_data = DBValue::from_vec(::rlp::encode(&account).into_vec());
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/snapshot/tests/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ fn get_code_from_prev_chunk() {
// first one will have code inlined,
// second will just have its hash.
let thin_rlp = acc_stream.out();
let acc: BasicAccount = ::rlp::decode(&thin_rlp);
let acc: BasicAccount = ::rlp::decode(&thin_rlp).expect("error decoding basic account");

let mut make_chunk = |acc, hash| {
let mut db = MemoryDB::new();
Expand Down
26 changes: 22 additions & 4 deletions ethcore/src/state/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,13 @@ impl Account {

/// Create a new account from RLP.
pub fn from_rlp(rlp: &[u8]) -> Account {
let basic: BasicAccount = ::rlp::decode(rlp);
basic.into()
match ::rlp::decode::<BasicAccount>(rlp) {
Ok(basic_account) => basic_account.into(),
Err(e) => {
error!("from_rlp, Rlp decoding error={}",e);
panic!("from_rlp, Rlp decoding error={}",e)
}
}
}

/// Create a new contract account.
Expand Down Expand Up @@ -203,7 +208,14 @@ impl Account {
}
let db = SecTrieDB::new(db, &self.storage_root)?;

let item: U256 = db.get_with(key, ::rlp::decode)?.unwrap_or_else(U256::zero);
let unwrapping_decoder: fn(&[u8]) -> U256 = |bytes: &[u8]| {
match ::rlp::decode(bytes) {
Ok(u256) => u256,
Err(_) => U256::zero()
}
};

let item: U256 = db.get_with(key, unwrapping_decoder)?.unwrap_or_else(U256::zero);
let value: H256 = item.into();
self.storage_cache.borrow_mut().insert(key.clone(), value.clone());
Ok(value)
Expand Down Expand Up @@ -478,7 +490,13 @@ impl Account {

let trie = TrieDB::new(db, &self.storage_root)?;
let item: U256 = {
let query = (&mut recorder, ::rlp::decode);
let unwrapping_decoder: fn(&[u8]) -> U256 = |bytes: &[u8]| {
match ::rlp::decode(bytes) {
Ok(u256) => u256,
Err(_) => U256::zero()
}
};
let query = (&mut recorder, unwrapping_decoder);
trie.get_with(&storage_key, query)?.unwrap_or_else(U256::zero)
};

Expand Down
5 changes: 4 additions & 1 deletion ethcore/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,10 @@ impl<B: Backend> State<B> {
let mut recorder = Recorder::new();
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
let maybe_account: Option<BasicAccount> = {
let query = (&mut recorder, ::rlp::decode);
let panicky_decoder = |bytes: &[u8]| {
::rlp::decode(bytes).expect(&format!("prove_account, could not query trie for account key={}", &account_key))
};
let query = (&mut recorder, panicky_decoder);
trie.get_with(&account_key, query)?
};
let account = maybe_account.unwrap_or_else(|| BasicAccount {
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/trace/types/flat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ mod tests {
]);

let encoded = ::rlp::encode(&block_traces);
let decoded = ::rlp::decode(&encoded);
let decoded = ::rlp::decode(&encoded).expect("error decoding block traces");
assert_eq!(block_traces, decoded);
}
}
5 changes: 3 additions & 2 deletions ethcore/transaction/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ mod tests {

#[test]
fn sender_test() {
let t: UnverifiedTransaction = rlp::decode(&::rustc_hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap());
let bytes = ::rustc_hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap();
let t: UnverifiedTransaction = rlp::decode(&bytes).expect("decoding UnverifiedTransaction failed");
assert_eq!(t.data, b"");
assert_eq!(t.gas, U256::from(0x5208u64));
assert_eq!(t.gas_price, U256::from(0x01u64));
Expand Down Expand Up @@ -645,7 +646,7 @@ mod tests {
use rustc_hex::FromHex;

let test_vector = |tx_data: &str, address: &'static str| {
let signed = rlp::decode(&FromHex::from_hex(tx_data).unwrap());
let signed = rlp::decode(&FromHex::from_hex(tx_data).unwrap()).expect("decoding tx data failed");
let signed = SignedTransaction::new(signed).unwrap();
assert_eq!(signed.sender(), address.into());
println!("chainid: {:?}", signed.chain_id());
Expand Down
4 changes: 2 additions & 2 deletions ethcore/types/src/receipt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ mod tests {
);
let encoded = ::rlp::encode(&r);
assert_eq!(&encoded[..], &expected[..]);
let decoded: Receipt = ::rlp::decode(&encoded);
let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed");
assert_eq!(decoded, r);
}

Expand All @@ -211,7 +211,7 @@ mod tests {
);
let encoded = ::rlp::encode(&r);
assert_eq!(&encoded[..], &expected[..]);
let decoded: Receipt = ::rlp::decode(&encoded);
let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed");
assert_eq!(decoded, r);
}
}
Loading