diff --git a/substrate/core/client/db/src/lib.rs b/substrate/core/client/db/src/lib.rs index eebd69d88dd2abdbd50f61ff10bcd352584f9b23..e73d3df62c9c0b87a8ff51022d827b52753f2c64 100644 --- a/substrate/core/client/db/src/lib.rs +++ b/substrate/core/client/db/src/lib.rs @@ -883,7 +883,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { transaction, columns::KEY_LOOKUP, r.number - ); + )?; } // canonicalize: set the number lookup to map to this block's hash. @@ -894,18 +894,18 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { columns::KEY_LOOKUP, e.number, e.hash - ); + )?; } } - let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1); + let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?; transaction.put(columns::META, meta_keys::BEST_BLOCK, &lookup_key); utils::insert_number_to_key_mapping( transaction, columns::KEY_LOOKUP, best_to.0, best_to.1, - ); + )?; Ok((enacted, retracted)) } @@ -946,7 +946,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { if let Some(justification) = justification { transaction.put( columns::JUSTIFICATION, - &utils::number_and_hash_to_lookup_key(number, hash), + &utils::number_and_hash_to_lookup_key(number, hash)?, &justification.encode(), ); } @@ -1021,7 +1021,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { let number = pending_block.header.number().clone(); // blocks are keyed by number + hash. - let lookup_key = utils::number_and_hash_to_lookup_key(number, hash); + let lookup_key = utils::number_and_hash_to_lookup_key(number, hash)?; let (enacted, retracted) = if pending_block.leaf_state.is_best() { self.set_head_with_transaction(&mut transaction, parent_hash, (number, hash))? @@ -1034,7 +1034,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { columns::KEY_LOOKUP, number, hash, - ); + )?; transaction.put(columns::HEADER, &lookup_key, &pending_block.header.encode()); if let Some(body) = pending_block.body { @@ -1177,7 +1177,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { if self.storage.state_db.best_canonical().map(|c| f_num.saturated_into::<u64>() > c).unwrap_or(true) { let parent_hash = f_header.parent_hash().clone(); - let lookup_key = utils::number_and_hash_to_lookup_key(f_num, f_hash.clone()); + let lookup_key = utils::number_and_hash_to_lookup_key(f_num, f_hash.clone())?; transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, &lookup_key); let commit = self.storage.state_db.canonicalize_block(&f_hash) @@ -1344,7 +1344,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe let hash = self.blockchain.hash(best)?.ok_or_else( || client::error::Error::UnknownBlock( format!("Error reverting to {}. Block hash not found.", best)))?; - let key = utils::number_and_hash_to_lookup_key(best.clone(), &hash); + let key = utils::number_and_hash_to_lookup_key(best.clone(), &hash)?; transaction.put(columns::META, meta_keys::BEST_BLOCK, &key); transaction.delete(columns::KEY_LOOKUP, removed.hash().as_ref()); children::remove_children(&mut transaction, columns::META, meta_keys::CHILDREN_PREFIX, hash); diff --git a/substrate/core/client/db/src/light.rs b/substrate/core/client/db/src/light.rs index 7d2f1e62d311f9ad89b942b3ff6e311b2c7f4208..95f50ab6f63171230303318bdc9642681a3aae26 100644 --- a/substrate/core/client/db/src/light.rs +++ b/substrate/core/client/db/src/light.rs @@ -211,7 +211,7 @@ impl<Block: BlockT> LightStorage<Block> { /// should be the parent of `best_to`. In the case where we set an existing block /// to be best, `route_to` should equal to `best_to`. fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor<Block>, Block::Hash)) -> Result<(), client::error::Error> { - let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1); + let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?; // handle reorg. let meta = self.meta.read(); @@ -234,7 +234,7 @@ impl<Block: BlockT> LightStorage<Block> { transaction, columns::KEY_LOOKUP, retracted.number - ); + )?; } for enacted in tree_route.enacted() { @@ -243,7 +243,7 @@ impl<Block: BlockT> LightStorage<Block> { columns::KEY_LOOKUP, enacted.number, enacted.hash - ); + )?; } } @@ -253,7 +253,7 @@ impl<Block: BlockT> LightStorage<Block> { columns::KEY_LOOKUP, best_to.0, best_to.1, - ); + )?; Ok(()) } @@ -274,7 +274,7 @@ impl<Block: BlockT> LightStorage<Block> { ).into()) } - let lookup_key = utils::number_and_hash_to_lookup_key(header.number().clone(), hash); + let lookup_key = utils::number_and_hash_to_lookup_key(header.number().clone(), hash)?; transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, &lookup_key); // build new CHT(s) if required @@ -293,7 +293,7 @@ impl<Block: BlockT> LightStorage<Block> { )?; transaction.put( columns::CHT, - &cht_key(HEADER_CHT_PREFIX, new_cht_start), + &cht_key(HEADER_CHT_PREFIX, new_cht_start)?, new_header_cht_root.as_ref() ); @@ -311,7 +311,7 @@ impl<Block: BlockT> LightStorage<Block> { )?; transaction.put( columns::CHT, - &cht_key(CHANGES_TRIE_CHT_PREFIX, new_cht_start), + &cht_key(CHANGES_TRIE_CHT_PREFIX, new_cht_start)?, new_changes_trie_cht_root.as_ref() ); } @@ -331,7 +331,7 @@ impl<Block: BlockT> LightStorage<Block> { columns::KEY_LOOKUP, prune_block, hash - ); + )?; transaction.delete(columns::HEADER, &lookup_key); } prune_block += One::one(); @@ -358,7 +358,7 @@ impl<Block: BlockT> LightStorage<Block> { let cht_number = cht::block_to_cht_number(cht_size, block).ok_or_else(no_cht_for_block)?; let cht_start = cht::start_number(cht_size, cht_number); - self.db.get(columns::CHT, &cht_key(cht_type, cht_start)).map_err(db_err)? + self.db.get(columns::CHT, &cht_key(cht_type, cht_start)?).map_err(db_err)? .ok_or_else(no_cht_for_block) .and_then(|hash| Block::Hash::decode(&mut &*hash).ok_or_else(no_cht_for_block)) } @@ -414,7 +414,7 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block> } // blocks are keyed by number + hash. - let lookup_key = utils::number_and_hash_to_lookup_key(number, &hash); + let lookup_key = utils::number_and_hash_to_lookup_key(number, &hash)?; if leaf_state.is_best() { self.set_head_with_transaction(&mut transaction, parent_hash, (number, hash))?; @@ -425,7 +425,7 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block> columns::KEY_LOOKUP, number, hash, - ); + )?; transaction.put(columns::HEADER, &lookup_key, &header.encode()); let is_genesis = number.is_zero(); @@ -562,10 +562,10 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block> } /// Build the key for inserting header-CHT at given block. -fn cht_key<N: TryInto<u32>>(cht_type: u8, block: N) -> [u8; 5] { +fn cht_key<N: TryInto<u32>>(cht_type: u8, block: N) -> ClientResult<[u8; 5]> { let mut key = [cht_type; 5]; - key[1..].copy_from_slice(&utils::number_index_key(block)); - key + key[1..].copy_from_slice(&utils::number_index_key(block)?); + Ok(key) } #[cfg(test)] diff --git a/substrate/core/client/db/src/utils.rs b/substrate/core/client/db/src/utils.rs index 39862dba8575090a8d03bd2ac71ca8b8da004bcd..e0e36c6dbce19ec6c1d5b4a21ec5badd2eaa0e6d 100644 --- a/substrate/core/client/db/src/utils.rs +++ b/substrate/core/client/db/src/utils.rs @@ -31,8 +31,8 @@ use parity_codec::Decode; use trie::DBValue; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{ - Block as BlockT, Header as HeaderT, Zero, UniqueSaturatedFrom, - UniqueSaturatedInto, CheckedConversion + Block as BlockT, Header as HeaderT, Zero, + UniqueSaturatedFrom, UniqueSaturatedInto, }; #[cfg(feature = "kvdb-rocksdb")] use crate::DatabaseSettings; @@ -84,25 +84,31 @@ pub type NumberIndexKey = [u8; 4]; /// /// In the current database schema, this kind of key is only used for /// lookups into an index, NOT for storing header data or others. -pub fn number_index_key<N: TryInto<u32>>(n: N) -> NumberIndexKey { - let n = n.checked_into::<u32>().unwrap(); - [ +pub fn number_index_key<N: TryInto<u32>>(n: N) -> client::error::Result<NumberIndexKey> { + let n = n.try_into().map_err(|_| + client::error::Error::Backend("Block number cannot be converted to u32".into()) + )?; + + Ok([ (n >> 24) as u8, ((n >> 16) & 0xff) as u8, ((n >> 8) & 0xff) as u8, (n & 0xff) as u8 - ] + ]) } /// Convert number and hash into long lookup key for blocks that are /// not in the canonical chain. -pub fn number_and_hash_to_lookup_key<N, H>(number: N, hash: H) -> Vec<u8> where +pub fn number_and_hash_to_lookup_key<N, H>( + number: N, + hash: H, +) -> client::error::Result<Vec<u8>> where N: TryInto<u32>, - H: AsRef<[u8]> + H: AsRef<[u8]>, { - let mut lookup_key = number_index_key(number).to_vec(); + let mut lookup_key = number_index_key(number)?.to_vec(); lookup_key.extend_from_slice(hash.as_ref()); - lookup_key + Ok(lookup_key) } /// Convert block lookup key into block number. @@ -124,8 +130,9 @@ pub fn remove_number_to_key_mapping<N: TryInto<u32>>( transaction: &mut DBTransaction, key_lookup_col: Option<u32>, number: N, -) { - transaction.delete(key_lookup_col, number_index_key(number).as_ref()) +) -> client::error::Result<()> { + transaction.delete(key_lookup_col, number_index_key(number)?.as_ref()); + Ok(()) } /// Remove key mappings. @@ -134,9 +141,10 @@ pub fn remove_key_mappings<N: TryInto<u32>, H: AsRef<[u8]>>( key_lookup_col: Option<u32>, number: N, hash: H, -) { - remove_number_to_key_mapping(transaction, key_lookup_col, number); +) -> client::error::Result<()> { + remove_number_to_key_mapping(transaction, key_lookup_col, number)?; transaction.delete(key_lookup_col, hash.as_ref()); + Ok(()) } /// Place a number mapping into the database. This maps number to current perceived @@ -146,12 +154,13 @@ pub fn insert_number_to_key_mapping<N: TryInto<u32> + Clone, H: AsRef<[u8]>>( key_lookup_col: Option<u32>, number: N, hash: H, -) { +) -> client::error::Result<()> { transaction.put_vec( key_lookup_col, - number_index_key(number.clone()).as_ref(), - number_and_hash_to_lookup_key(number, hash), - ) + number_index_key(number.clone())?.as_ref(), + number_and_hash_to_lookup_key(number, hash)?, + ); + Ok(()) } /// Insert a hash to key mapping in the database. @@ -160,12 +169,13 @@ pub fn insert_hash_to_key_mapping<N: TryInto<u32>, H: AsRef<[u8]> + Clone>( key_lookup_col: Option<u32>, number: N, hash: H, -) { +) -> client::error::Result<()> { transaction.put_vec( key_lookup_col, hash.clone().as_ref(), - number_and_hash_to_lookup_key(number, hash), - ) + number_and_hash_to_lookup_key(number, hash)?, + ); + Ok(()) } /// Convert block id to block lookup key. @@ -182,7 +192,7 @@ pub fn block_id_to_lookup_key<Block>( let res = match id { BlockId::Number(n) => db.get( key_lookup_col, - number_index_key(n).as_ref(), + number_index_key(n)?.as_ref(), ), BlockId::Hash(h) => db.get(key_lookup_col, h.as_ref()), }; @@ -318,3 +328,19 @@ pub fn read_meta<Block>(db: &dyn KeyValueDB, col_meta: Option<u32>, col_header: genesis_hash, }) } + +#[cfg(test)] +mod tests { + use super::*; + use runtime_primitives::testing::{Block as RawBlock, ExtrinsicWrapper}; + type Block = RawBlock<ExtrinsicWrapper<u32>>; + + #[test] + fn number_index_key_doesnt_panic() { + let id = BlockId::<Block>::Number(72340207214430721); + match id { + BlockId::Number(n) => number_index_key(n).expect_err("number should overflow u32"), + _ => unreachable!(), + }; + } +} diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 5122cbac032624b7ada891e8a9f975130afee77f..9241d8fb0a418c1f8b95300990eb0161c4222b41 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -2633,4 +2633,14 @@ pub(crate) mod tests { b3.hash(), ); } + + #[test] + fn get_header_by_block_number_doesnt_panic() { + let client = test_client::new(); + + // backend uses u32 for block numbers, make sure we don't panic when + // trying to convert + let id = BlockId::<Block>::Number(72340207214430721); + client.header(&id).expect_err("invalid block number overflows u32"); + } }