Skip to content

Commit 3fefc57

Browse files
alexgghbkchrgithub-actions[bot]
committed
add block hashes to the randomness used by hashmaps and friends in validation context (#9127)
#8606 paritytech/trie#221 replaced the usage of BTreeMap with HashMaps in validation context. The keys are already derived with a cryptographic hash function from user data, so users should not be able to manipulate it. To be on safe side this PR also modifies the TrieCache, TrieRecorder and MemoryDB to use a hasher that on top of the default generated randomness also adds randomness generated from the hash of the relaychain and that of the parachain blocks, which is not something users can control or guess ahead of time. --------- Signed-off-by: Alexandru Gheorghe <[email protected]> Co-authored-by: Bastian Köcher <[email protected]> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> (cherry picked from commit 7058819)
1 parent c3bfa7a commit 3fefc57

File tree

16 files changed

+283
-35
lines changed

16 files changed

+283
-35
lines changed

Cargo.lock

Lines changed: 9 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,7 @@ first-pallet = { package = "polkadot-sdk-docs-first-pallet", path = "docs/sdk/pa
801801
first-runtime = { package = "polkadot-sdk-docs-first-runtime", path = "docs/sdk/packages/guides/first-runtime", default-features = false }
802802
flate2 = { version = "1.0" }
803803
fnv = { version = "1.0.6" }
804+
foldhash = { version = "0.1.5", default-features = false }
804805
fork-tree = { path = "substrate/utils/fork-tree", default-features = false }
805806
forwarded-header-value = { version = "0.1.1" }
806807
fraction = { version = "0.13.1" }
@@ -895,7 +896,7 @@ log = { version = "0.4.22", default-features = false }
895896
macro_magic = { version = "0.5.1" }
896897
maplit = { version = "1.0.2" }
897898
memmap2 = { version = "0.9.3" }
898-
memory-db = { version = "0.33.0", default-features = false }
899+
memory-db = { version = "0.34.0", default-features = false }
899900
merkleized-metadata = { version = "0.5.0" }
900901
merlin = { version = "3.0", default-features = false }
901902
messages-relay = { path = "bridges/relays/messages" }
@@ -1425,7 +1426,7 @@ tracing-futures = { version = "0.2.4" }
14251426
tracing-log = { version = "0.2.0" }
14261427
tracing-subscriber = { version = "0.3.18" }
14271428
tracking-allocator = { path = "polkadot/node/tracking-allocator", default-features = false, package = "staging-tracking-allocator" }
1428-
trie-bench = { version = "0.41.0" }
1429+
trie-bench = { version = "0.42.0" }
14291430
trie-db = { version = "0.30.0", default-features = false }
14301431
trie-root = { version = "0.18.0", default-features = false }
14311432
trie-standardmap = { version = "0.16.0" }

cumulus/pallets/parachain-system/src/validate_block/implementation.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ use frame_support::{
3636
};
3737
use sp_core::storage::{ChildInfo, StateVersion};
3838
use sp_externalities::{set_and_run_with_externalities, Externalities};
39-
use sp_io::KillStorageResult;
39+
use sp_io::{hashing::blake2_128, KillStorageResult};
4040
use sp_runtime::traits::{
4141
Block as BlockT, ExtrinsicCall, ExtrinsicLike, HashingFor, Header as HeaderT,
4242
};
43+
4344
use sp_state_machine::OverlayedChanges;
4445
use sp_trie::ProofSizeProvider;
4546
use trie_recorder::SizeOnlyRecorderProvider;
@@ -142,6 +143,12 @@ where
142143
let block_data = codec::decode_from_bytes::<ParachainBlockData<B>>(block_data)
143144
.expect("Invalid parachain block data");
144145

146+
// Initialize hashmaps randomness.
147+
sp_trie::add_extra_randomness(build_seed_from_head_data(
148+
&block_data,
149+
relay_parent_storage_root,
150+
));
151+
145152
let mut parent_header =
146153
codec::decode_from_bytes::<B::Header>(parachain_head.clone()).expect("Invalid parent head");
147154

@@ -393,6 +400,27 @@ fn validate_validation_data(
393400
);
394401
}
395402

403+
/// Build a seed from the head data of the parachain block.
404+
///
405+
/// Uses both the relay parent storage root and the hash of the blocks
406+
/// in the block data, to make sure the seed changes every block and that
407+
/// the user cannot find about it ahead of time.
408+
fn build_seed_from_head_data<B: BlockT>(
409+
block_data: &ParachainBlockData<B>,
410+
relay_parent_storage_root: crate::relay_chain::Hash,
411+
) -> [u8; 16] {
412+
let mut bytes_to_hash = Vec::with_capacity(
413+
block_data.blocks().len() * size_of::<B::Hash>() + size_of::<crate::relay_chain::Hash>(),
414+
);
415+
416+
bytes_to_hash.extend_from_slice(relay_parent_storage_root.as_ref());
417+
block_data.blocks().iter().for_each(|block| {
418+
bytes_to_hash.extend_from_slice(block.header().hash().as_ref());
419+
});
420+
421+
blake2_128(&bytes_to_hash)
422+
}
423+
396424
/// Run the given closure with the externalities and recorder set.
397425
fn run_with_externalities_and_recorder<Block: BlockT, R, F: FnOnce() -> R>(
398426
backend: &impl sp_state_machine::Backend<HashingFor<Block>>,

cumulus/pallets/parachain-system/src/validate_block/trie_cache.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ use alloc::boxed::Box;
2020
use core::cell::{RefCell, RefMut};
2121
use hashbrown::{hash_map::Entry, HashMap};
2222
use sp_state_machine::TrieCacheProvider;
23-
use sp_trie::NodeCodec;
23+
use sp_trie::{NodeCodec, RandomState};
2424
use trie_db::{node::NodeOwned, Hasher};
2525

2626
/// Special purpose trie cache implementation that is able to cache an unlimited number
2727
/// of values. To be used in `validate_block` to serve values and nodes that
2828
/// have already been loaded and decoded from the storage proof.
2929
pub struct TrieCache<'a, H: Hasher> {
30-
node_cache: RefMut<'a, HashMap<H::Out, NodeOwned<H::Out>>>,
31-
value_cache: Option<RefMut<'a, HashMap<Box<[u8]>, trie_db::CachedValue<H::Out>>>>,
30+
node_cache: RefMut<'a, HashMap<H::Out, NodeOwned<H::Out>, RandomState>>,
31+
value_cache: Option<RefMut<'a, HashMap<Box<[u8]>, trie_db::CachedValue<H::Out>, RandomState>>>,
3232
}
3333

3434
impl<'a, H: Hasher> trie_db::TrieCache<NodeCodec<H>> for TrieCache<'a, H> {
@@ -65,14 +65,16 @@ impl<'a, H: Hasher> trie_db::TrieCache<NodeCodec<H>> for TrieCache<'a, H> {
6565

6666
/// Provider of [`TrieCache`] instances.
6767
pub struct CacheProvider<H: Hasher> {
68-
node_cache: RefCell<HashMap<H::Out, NodeOwned<H::Out>>>,
68+
node_cache: RefCell<HashMap<H::Out, NodeOwned<H::Out>, RandomState>>,
6969
/// Cache: `storage_root` => `storage_key` => `value`.
7070
///
7171
/// One `block` can for example use multiple tries (child tries) and we need to distinguish the
7272
/// cached (`storage_key`, `value`) between them. For this we are using the `storage_root` to
7373
/// distinguish them (even if the storage root is the same for two child tries, it just means
7474
/// that both are exactly the same trie and there would happen no collision).
75-
value_cache: RefCell<HashMap<H::Out, HashMap<Box<[u8]>, trie_db::CachedValue<H::Out>>>>,
75+
value_cache: RefCell<
76+
HashMap<H::Out, HashMap<Box<[u8]>, trie_db::CachedValue<H::Out>, RandomState>, RandomState>,
77+
>,
7678
}
7779

7880
impl<H: Hasher> CacheProvider<H> {

cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@ use alloc::rc::Rc;
2626

2727
use core::cell::{RefCell, RefMut};
2828
use hashbrown::{HashMap, HashSet};
29-
use sp_trie::{NodeCodec, ProofSizeProvider, StorageProof};
29+
use sp_trie::{NodeCodec, ProofSizeProvider, RandomState, StorageProof};
3030
use trie_db::{Hasher, RecordedForKey, TrieAccess};
3131

3232
/// A trie recorder that only keeps track of the proof size.
3333
///
3434
/// The internal size counting logic should align
3535
/// with ['sp_trie::recorder::Recorder'].
3636
pub struct SizeOnlyRecorder<'a, H: Hasher> {
37-
seen_nodes: RefMut<'a, HashSet<H::Out>>,
37+
seen_nodes: RefMut<'a, HashSet<H::Out, RandomState>>,
3838
encoded_size: RefMut<'a, usize>,
39-
recorded_keys: RefMut<'a, HashMap<Rc<[u8]>, RecordedForKey>>,
39+
recorded_keys: RefMut<'a, HashMap<Rc<[u8]>, RecordedForKey, RandomState>>,
4040
}
4141

4242
impl<'a, H: trie_db::Hasher> trie_db::TrieRecorder<H::Out> for SizeOnlyRecorder<'a, H> {
@@ -90,9 +90,9 @@ impl<'a, H: trie_db::Hasher> trie_db::TrieRecorder<H::Out> for SizeOnlyRecorder<
9090

9191
#[derive(Clone)]
9292
pub struct SizeOnlyRecorderProvider<H: Hasher> {
93-
seen_nodes: Rc<RefCell<HashSet<H::Out>>>,
93+
seen_nodes: Rc<RefCell<HashSet<H::Out, RandomState>>>,
9494
encoded_size: Rc<RefCell<usize>>,
95-
recorded_keys: Rc<RefCell<HashMap<Rc<[u8]>, RecordedForKey>>>,
95+
recorded_keys: Rc<RefCell<HashMap<Rc<[u8]>, RecordedForKey, RandomState>>>,
9696
}
9797

9898
impl<H: Hasher> Default for SizeOnlyRecorderProvider<H> {

prdoc/pr_9127.prdoc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
title: add block hashes to the randomness used by hashmaps and friends in validation
2+
context
3+
doc:
4+
- audience: Node Dev
5+
description: |-
6+
https://github.com/paritytech/polkadot-sdk/pull/8606 https://github.com/paritytech/trie/pull/221 replaced the usage of BTreeMap with HashMaps in validation context. The keys are already derived with a cryptographic hash function from user data, so users should not be able to manipulate it.
7+
8+
To be on safe side this PR also modifies the TrieCache, TrieRecorder and MemoryDB to use a hasher that on top of the default generated randomness also adds randomness generated from the hash of the relaychain and that of the parachain blocks, which is not something users can control or guess ahead of time.
9+
crates:
10+
- name: bridge-runtime-common
11+
bump: minor
12+
- name: pallet-bridge-messages
13+
bump: minor
14+
- name: bp-test-utils
15+
bump: minor
16+
- name: cumulus-pallet-parachain-system
17+
bump: minor
18+
- name: sp-state-machine
19+
bump: minor
20+
- name: sp-trie
21+
bump: minor
22+
- name: pallet-session
23+
bump: minor
24+
- name: sp-runtime
25+
bump: minor

substrate/frame/session/src/historical/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use sp_session::{MembershipProof, ValidatorCount};
4141
use sp_staking::SessionIndex;
4242
use sp_trie::{
4343
trie_types::{TrieDBBuilder, TrieDBMutBuilderV0},
44-
LayoutV0, MemoryDB, Recorder, StorageProof, Trie, TrieMut, TrieRecorder,
44+
LayoutV0, MemoryDB, RandomState, Recorder, StorageProof, Trie, TrieMut, TrieRecorder,
4545
};
4646

4747
use frame_support::{
@@ -264,7 +264,7 @@ impl<T: Config> ProvingTrie<T> {
264264
where
265265
I: IntoIterator<Item = (T::ValidatorId, T::FullIdentification)>,
266266
{
267-
let mut db = MemoryDB::default();
267+
let mut db = MemoryDB::with_hasher(RandomState::default());
268268
let mut root = Default::default();
269269

270270
{

substrate/primitives/runtime/src/proving_trie/base16.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use alloc::vec::Vec;
3030
use codec::MaxEncodedLen;
3131
use sp_trie::{
3232
trie_types::{TrieDBBuilder, TrieDBMutBuilderV1},
33-
LayoutV1, MemoryDB, Trie, TrieMut,
33+
LayoutV1, MemoryDB, RandomState, Trie, TrieMut,
3434
};
3535

3636
/// A helper structure for building a basic base-16 merkle trie and creating compact proofs for that
@@ -77,7 +77,7 @@ where
7777
where
7878
I: IntoIterator<Item = (Key, Value)>,
7979
{
80-
let mut db = MemoryDB::default();
80+
let mut db = MemoryDB::with_hasher(RandomState::default());
8181
let mut root = Default::default();
8282

8383
{

substrate/primitives/state-machine/src/backend.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use hash_db::Hasher;
3030
use sp_core::storage::{ChildInfo, StateVersion, TrackedStorageKey};
3131
#[cfg(feature = "std")]
3232
use sp_core::traits::RuntimeCode;
33-
use sp_trie::{MerkleValue, PrefixedMemoryDB};
33+
use sp_trie::{MerkleValue, PrefixedMemoryDB, RandomState};
3434

3535
/// A struct containing arguments for iterating over the storage.
3636
#[derive(Default)]
@@ -301,7 +301,7 @@ pub trait Backend<H: Hasher>: core::fmt::Debug {
301301
where
302302
H::Out: Ord + Encode,
303303
{
304-
let mut txs = BackendTransaction::default();
304+
let mut txs = BackendTransaction::with_hasher(RandomState::default());
305305
let mut child_roots: Vec<_> = Default::default();
306306
// child first
307307
for (child_info, child_delta) in child_deltas {

substrate/primitives/state-machine/src/in_memory_backend.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use alloc::{collections::BTreeMap, vec::Vec};
2525
use codec::Codec;
2626
use hash_db::Hasher;
2727
use sp_core::storage::{ChildInfo, StateVersion, Storage};
28-
use sp_trie::{empty_trie_root, LayoutV1, PrefixedMemoryDB};
28+
use sp_trie::{empty_trie_root, LayoutV1, PrefixedMemoryDB, RandomState};
2929

3030
#[cfg(feature = "std")]
3131
use std::collections::HashMap as MapType;
@@ -40,7 +40,11 @@ where
4040
H::Out: Codec + Ord,
4141
{
4242
// V1 is same as V0 for an empty trie.
43-
TrieBackendBuilder::new(Default::default(), empty_trie_root::<LayoutV1<H>>()).build()
43+
TrieBackendBuilder::new(
44+
PrefixedMemoryDB::with_hasher(RandomState::default()),
45+
empty_trie_root::<LayoutV1<H>>(),
46+
)
47+
.build()
4448
}
4549

4650
impl<H: Hasher> TrieBackend<PrefixedMemoryDB<H>, H>

0 commit comments

Comments
 (0)