mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 00:31:07 +00:00
Inner hashing of value in state trie (runtime versioning). (#9732)
* starting * Updated from other branch. * setting flag * flag in storage struct * fix flagging to access and insert. * added todo to fix * also missing serialize meta to storage proof * extract meta. * Isolate old trie layout. * failing test that requires storing in meta when old hash scheme is used. * old hash compatibility * Db migrate. * runing tests with both states when interesting. * fix chain spec test with serde default. * export state (missing trie function). * Pending using new branch, lacking genericity on layout resolution. * extract and set global meta * Update to branch 4 * fix iterator with root flag (no longer insert node). * fix trie root hashing of root * complete basic backend. * Remove old_hash meta from proof that do not use inner_hashing. * fix trie test for empty (force layout on empty deltas). * Root update fix. * debug on meta * Use trie key iteration that do not include value in proofs. * switch default test ext to use inner hash. * small integration test, and fix tx cache mgmt in ext. test failing * Proof scenario at state-machine level. * trace for db upgrade * try different param * act more like iter_from. * Bigger batches. * Update trie dependency. * drafting codec changes and refact * before removing unused branch no value alt hashing. more work todo rename all flag var to alt_hash, and remove extrinsic replace by storage query at every storage_root call. * alt hashing only for branch with value. * fix trie tests * Hash of value include the encoded size. * removing fields(broken) * fix trie_stream to also include value length in inner hash. * triedbmut only using alt type if inner hashing. * trie_stream to also only use alt hashing type when actually alt hashing. * Refactor meta state, logic should work with change of trie treshold. * Remove NoMeta variant. * Remove state_hashed trigger specific functions. * pending switching to using threshold, new storage root api does not make much sense. * refactoring to use state from backend (not possible payload changes). * Applying from previous state * Remove default from storage, genesis need a special build. * rem empty space * Catch problem: when using triedb with default: we should not revert nodes: otherwhise thing as trie codec cannot decode-encode without changing state. * fix compilation * Right logic to avoid switch on reencode when default layout. * Clean up some todos * remove trie meta from root upstream * update upstream and fix benches. * split some long lines. * UPdate trie crate to work with new design. * Finish update to refactored upstream. * update to latest triedb changes. * Clean up. * fix executor test. * rust fmt from master. * rust format. * rustfmt * fix * start host function driven versioning * update state-machine part * still need access to state version from runtime * state hash in mem: wrong * direction likely correct, but passing call to code exec for genesis init seem awkward. * state version serialize in runtime, wrong approach, just initialize it with no threshold for core api < 4 seems more proper. * stateversion from runtime version (core api >= 4). * update trie, fix tests * unused import * clean some TODOs * Require RuntimeVersionOf for executor * use RuntimeVersionOf to resolve genesis state version. * update runtime version test * fix state-machine tests * TODO * Use runtime version from storage wasm with fast sync. * rustfmt * fmt * fix test * revert useless changes. * clean some unused changes * fmt * removing useless trait function. * remove remaining reference to state_hash * fix some imports * Follow chain state version management. * trie update, fix and constant threshold for trie layouts. * update deps * Update to latest trie pr changes. * fix benches * Verify proof requires right layout. * update trie_root * Update trie deps to latest * Update to latest trie versioning * Removing patch * update lock * extrinsic for sc-service-test using layout v0. * Adding RuntimeVersionOf to CallExecutor works. * fmt * error when resolving version and no wasm in storage. * use existing utils to instantiate runtime code. * Patch to delay runtime switch. * Revert "Patch to delay runtime switch." This reverts commit 67e55fee468f1a0cda853f5362b22e0d775786da. * useless closure * remove remaining state_hash variables. * Remove outdated comment * useless inner hash * fmt * fmt and opt-in feature to apply state change. * feature gate core version, use new test feature for node and test node * Use a 'State' api version instead of Core one. * fix merge of test function * use blake macro. * Fix state api (require declaring the api in runtime). * Opt out feature, fix macro for io to select a given version instead of latest. * run test nodes on new state. * fix * Apply review change (docs and error). * fmt * use explicit runtime_interface in doc test * fix ui test * fix doc test * fmt * use default for path and specname when resolving version. * small review related changes. * doc value size requirement. * rename old_state feature * Remove macro changes * feature rename * state version as host function parameter * remove flag for client api * fix tests * switch storage chain proof to V1 * host functions, pass by state version enum * use WrappedRuntimeCode * start * state_version in runtime version * rust fmt * Update storage proof of max size. * fix runtime version rpc test * right intent of convert from compat * fix doc test * fix doc test * split proof * decode without replay, and remove some reexports. * Decode with compatibility by default. * switch state_version to u8. And remove RuntimeVersionBasis. * test * use api when reading embedded version * fix decode with apis * extract core version instead * test fix * unused import * review changes. Co-authored-by: kianenigma <kian@parity.io>
This commit is contained in:
@@ -45,31 +45,43 @@ pub use trie_db::proof::VerifyError;
|
||||
use trie_db::proof::{generate_proof, verify_proof};
|
||||
/// Various re-exports from the `trie-db` crate.
|
||||
pub use trie_db::{
|
||||
nibble_ops, CError, DBValue, Query, Recorder, Trie, TrieConfiguration, TrieDBIterator,
|
||||
nibble_ops,
|
||||
node::{NodePlan, ValuePlan},
|
||||
CError, DBValue, Query, Recorder, Trie, TrieConfiguration, TrieDBIterator, TrieDBKeyIterator,
|
||||
TrieLayout, TrieMut,
|
||||
};
|
||||
/// The Substrate format implementation of `TrieStream`.
|
||||
pub use trie_stream::TrieStream;
|
||||
|
||||
#[derive(Default)]
|
||||
/// substrate trie layout
|
||||
pub struct Layout<H>(sp_std::marker::PhantomData<H>);
|
||||
pub struct LayoutV0<H>(sp_std::marker::PhantomData<H>);
|
||||
|
||||
impl<H: Hasher> TrieLayout for Layout<H> {
|
||||
/// substrate trie layout, with external value nodes.
|
||||
pub struct LayoutV1<H>(sp_std::marker::PhantomData<H>);
|
||||
|
||||
impl<H> TrieLayout for LayoutV0<H>
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
const USE_EXTENSION: bool = false;
|
||||
const ALLOW_EMPTY: bool = true;
|
||||
const MAX_INLINE_VALUE: Option<u32> = None;
|
||||
|
||||
type Hash = H;
|
||||
type Codec = NodeCodec<Self::Hash>;
|
||||
}
|
||||
|
||||
impl<H: Hasher> TrieConfiguration for Layout<H> {
|
||||
impl<H> TrieConfiguration for LayoutV0<H>
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
fn trie_root<I, A, B>(input: I) -> <Self::Hash as Hasher>::Out
|
||||
where
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
trie_root::trie_root_no_extension::<H, TrieStream, _, _, _>(input)
|
||||
trie_root::trie_root_no_extension::<H, TrieStream, _, _, _>(input, Self::MAX_INLINE_VALUE)
|
||||
}
|
||||
|
||||
fn trie_root_unhashed<I, A, B>(input: I) -> Vec<u8>
|
||||
@@ -78,7 +90,52 @@ impl<H: Hasher> TrieConfiguration for Layout<H> {
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
trie_root::unhashed_trie_no_extension::<H, TrieStream, _, _, _>(input)
|
||||
trie_root::unhashed_trie_no_extension::<H, TrieStream, _, _, _>(
|
||||
input,
|
||||
Self::MAX_INLINE_VALUE,
|
||||
)
|
||||
}
|
||||
|
||||
fn encode_index(input: u32) -> Vec<u8> {
|
||||
codec::Encode::encode(&codec::Compact(input))
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> TrieLayout for LayoutV1<H>
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
const USE_EXTENSION: bool = false;
|
||||
const ALLOW_EMPTY: bool = true;
|
||||
const MAX_INLINE_VALUE: Option<u32> = Some(sp_core::storage::TRIE_VALUE_NODE_THRESHOLD);
|
||||
|
||||
type Hash = H;
|
||||
type Codec = NodeCodec<Self::Hash>;
|
||||
}
|
||||
|
||||
impl<H> TrieConfiguration for LayoutV1<H>
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
fn trie_root<I, A, B>(input: I) -> <Self::Hash as Hasher>::Out
|
||||
where
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
trie_root::trie_root_no_extension::<H, TrieStream, _, _, _>(input, Self::MAX_INLINE_VALUE)
|
||||
}
|
||||
|
||||
fn trie_root_unhashed<I, A, B>(input: I) -> Vec<u8>
|
||||
where
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
trie_root::unhashed_trie_no_extension::<H, TrieStream, _, _, _>(
|
||||
input,
|
||||
Self::MAX_INLINE_VALUE,
|
||||
)
|
||||
}
|
||||
|
||||
fn encode_index(input: u32) -> Vec<u8> {
|
||||
@@ -118,17 +175,22 @@ pub type TrieDBMut<'a, L> = trie_db::TrieDBMut<'a, L>;
|
||||
pub type Lookup<'a, L, Q> = trie_db::Lookup<'a, L, Q>;
|
||||
/// Hash type for a trie layout.
|
||||
pub type TrieHash<L> = <<L as TrieLayout>::Hash as Hasher>::Out;
|
||||
|
||||
/// This module is for non generic definition of trie type.
|
||||
/// Only the `Hasher` trait is generic in this case.
|
||||
pub mod trie_types {
|
||||
pub type Layout<H> = super::Layout<H>;
|
||||
use super::*;
|
||||
|
||||
/// Persistent trie database read-access interface for the a given hasher.
|
||||
pub type TrieDB<'a, H> = super::TrieDB<'a, Layout<H>>;
|
||||
/// Read only V1 and V0 are compatible, thus we always use V1.
|
||||
pub type TrieDB<'a, H> = super::TrieDB<'a, LayoutV1<H>>;
|
||||
/// Persistent trie database write-access interface for the a given hasher.
|
||||
pub type TrieDBMut<'a, H> = super::TrieDBMut<'a, Layout<H>>;
|
||||
pub type TrieDBMutV0<'a, H> = super::TrieDBMut<'a, LayoutV0<H>>;
|
||||
/// Persistent trie database write-access interface for the a given hasher.
|
||||
pub type TrieDBMutV1<'a, H> = super::TrieDBMut<'a, LayoutV1<H>>;
|
||||
/// Querying interface, as in `trie_db` but less generic.
|
||||
pub type Lookup<'a, H, Q> = trie_db::Lookup<'a, Layout<H>, Q>;
|
||||
pub type LookupV0<'a, H, Q> = trie_db::Lookup<'a, LayoutV0<H>, Q>;
|
||||
/// Querying interface, as in `trie_db` but less generic.
|
||||
pub type LookupV1<'a, H, Q> = trie_db::Lookup<'a, LayoutV1<H>, Q>;
|
||||
/// As in `trie_db`, but less generic, error type for the crate.
|
||||
pub type TrieError<H> = trie_db::TrieError<H, super::Error>;
|
||||
}
|
||||
@@ -141,16 +203,18 @@ pub mod trie_types {
|
||||
/// For a key `K` that is included in the `db` a proof of inclusion is generated.
|
||||
/// For a key `K` that is not included in the `db` a proof of non-inclusion is generated.
|
||||
/// These can be later checked in `verify_trie_proof`.
|
||||
pub fn generate_trie_proof<'a, L: TrieConfiguration, I, K, DB>(
|
||||
pub fn generate_trie_proof<'a, L, I, K, DB>(
|
||||
db: &DB,
|
||||
root: TrieHash<L>,
|
||||
keys: I,
|
||||
) -> Result<Vec<Vec<u8>>, Box<TrieError<L>>>
|
||||
where
|
||||
L: TrieConfiguration,
|
||||
I: IntoIterator<Item = &'a K>,
|
||||
K: 'a + AsRef<[u8]>,
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
|
||||
{
|
||||
// Can use default layout (read only).
|
||||
let trie = TrieDB::<L>::new(db, &root)?;
|
||||
generate_proof(&trie, keys)
|
||||
}
|
||||
@@ -163,17 +227,18 @@ where
|
||||
/// checked for inclusion in the proof.
|
||||
/// If the value is omitted (`(key, None)`), this key will be checked for non-inclusion in the
|
||||
/// proof.
|
||||
pub fn verify_trie_proof<'a, L: TrieConfiguration, I, K, V>(
|
||||
pub fn verify_trie_proof<'a, L, I, K, V>(
|
||||
root: &TrieHash<L>,
|
||||
proof: &[Vec<u8>],
|
||||
items: I,
|
||||
) -> Result<(), VerifyError<TrieHash<L>, error::Error>>
|
||||
) -> Result<(), VerifyError<TrieHash<L>, CError<L>>>
|
||||
where
|
||||
L: TrieConfiguration,
|
||||
I: IntoIterator<Item = &'a (K, Option<V>)>,
|
||||
K: 'a + AsRef<[u8]>,
|
||||
V: 'a + AsRef<[u8]>,
|
||||
{
|
||||
verify_proof::<Layout<L::Hash>, _, _, _>(root, proof, items)
|
||||
verify_proof::<L, _, _, _>(root, proof, items)
|
||||
}
|
||||
|
||||
/// Determine a trie root given a hash DB and delta values.
|
||||
@@ -207,28 +272,33 @@ where
|
||||
}
|
||||
|
||||
/// Read a value from the trie.
|
||||
pub fn read_trie_value<L: TrieConfiguration, DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>>(
|
||||
pub fn read_trie_value<L, DB>(
|
||||
db: &DB,
|
||||
root: &TrieHash<L>,
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>> {
|
||||
TrieDB::<L>::new(&*db, root)?.get(key).map(|x| x.map(|val| val.to_vec()))
|
||||
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>>
|
||||
where
|
||||
L: TrieConfiguration,
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
|
||||
{
|
||||
Ok(TrieDB::<L>::new(&*db, root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
|
||||
}
|
||||
|
||||
/// Read a value from the trie with given Query.
|
||||
pub fn read_trie_value_with<
|
||||
L: TrieConfiguration,
|
||||
Q: Query<L::Hash, Item = DBValue>,
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
|
||||
>(
|
||||
pub fn read_trie_value_with<L, Q, DB>(
|
||||
db: &DB,
|
||||
root: &TrieHash<L>,
|
||||
key: &[u8],
|
||||
query: Q,
|
||||
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>> {
|
||||
TrieDB::<L>::new(&*db, root)?
|
||||
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>>
|
||||
where
|
||||
L: TrieConfiguration,
|
||||
Q: Query<L::Hash, Item = DBValue>,
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
|
||||
{
|
||||
Ok(TrieDB::<L>::new(&*db, root)?
|
||||
.get_with(key, query)
|
||||
.map(|x| x.map(|val| val.to_vec()))
|
||||
.map(|x| x.map(|val| val.to_vec()))?)
|
||||
}
|
||||
|
||||
/// Determine the empty trie root.
|
||||
@@ -319,7 +389,7 @@ where
|
||||
}
|
||||
|
||||
/// Read a value from the child trie with given query.
|
||||
pub fn read_child_trie_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item = DBValue>, DB>(
|
||||
pub fn read_child_trie_value_with<L, Q, DB>(
|
||||
keyspace: &[u8],
|
||||
db: &DB,
|
||||
root_slice: &[u8],
|
||||
@@ -327,6 +397,8 @@ pub fn read_child_trie_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item =
|
||||
query: Q,
|
||||
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>>
|
||||
where
|
||||
L: TrieConfiguration,
|
||||
Q: Query<L::Hash, Item = DBValue>,
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
|
||||
{
|
||||
let mut root = TrieHash::<L>::default();
|
||||
@@ -444,11 +516,15 @@ where
|
||||
|
||||
/// Constants used into trie simplification codec.
|
||||
mod trie_constants {
|
||||
pub const EMPTY_TRIE: u8 = 0;
|
||||
pub const NIBBLE_SIZE_BOUND: usize = u16::MAX as usize;
|
||||
const FIRST_PREFIX: u8 = 0b_00 << 6;
|
||||
pub const NIBBLE_SIZE_BOUND: usize = u16::max_value() as usize;
|
||||
pub const LEAF_PREFIX_MASK: u8 = 0b_01 << 6;
|
||||
pub const BRANCH_WITHOUT_MASK: u8 = 0b_10 << 6;
|
||||
pub const BRANCH_WITH_MASK: u8 = 0b_11 << 6;
|
||||
pub const EMPTY_TRIE: u8 = FIRST_PREFIX | (0b_00 << 4);
|
||||
pub const ALT_HASHING_LEAF_PREFIX_MASK: u8 = FIRST_PREFIX | (0b_1 << 5);
|
||||
pub const ALT_HASHING_BRANCH_WITH_MASK: u8 = FIRST_PREFIX | (0b_01 << 4);
|
||||
pub const ESCAPE_COMPACT_HEADER: u8 = EMPTY_TRIE | 0b_00_01;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -461,7 +537,11 @@ mod tests {
|
||||
use trie_db::{DBValue, NodeCodec as NodeCodecT, Trie, TrieMut};
|
||||
use trie_standardmap::{Alphabet, StandardMap, ValueMode};
|
||||
|
||||
type Layout = super::Layout<Blake2Hasher>;
|
||||
type LayoutV0 = super::LayoutV0<Blake2Hasher>;
|
||||
type LayoutV1 = super::LayoutV1<Blake2Hasher>;
|
||||
|
||||
type MemoryDBMeta<H> =
|
||||
memory_db::MemoryDB<H, memory_db::HashKey<H>, trie_db::DBValue, MemTracker>;
|
||||
|
||||
fn hashed_null_node<T: TrieConfiguration>() -> TrieHash<T> {
|
||||
<T::Codec as NodeCodecT>::hashed_null_node()
|
||||
@@ -473,7 +553,7 @@ mod tests {
|
||||
let d = T::trie_root_unhashed(input.clone());
|
||||
println!("Data: {:#x?}, {:#x?}", d, Blake2Hasher::hash(&d[..]));
|
||||
let persistent = {
|
||||
let mut memdb = MemoryDB::default();
|
||||
let mut memdb = MemoryDBMeta::default();
|
||||
let mut root = Default::default();
|
||||
let mut t = TrieDBMut::<T>::new(&mut memdb, &mut root);
|
||||
for (x, y) in input.iter().rev() {
|
||||
@@ -486,7 +566,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn check_iteration<T: TrieConfiguration>(input: &Vec<(&[u8], &[u8])>) {
|
||||
let mut memdb = MemoryDB::default();
|
||||
let mut memdb = MemoryDBMeta::default();
|
||||
let mut root = Default::default();
|
||||
{
|
||||
let mut t = TrieDBMut::<T>::new(&mut memdb, &mut root);
|
||||
@@ -506,14 +586,21 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_input(input: &Vec<(&[u8], &[u8])>) {
|
||||
check_equivalent::<LayoutV0>(input);
|
||||
check_iteration::<LayoutV0>(input);
|
||||
check_equivalent::<LayoutV1>(input);
|
||||
check_iteration::<LayoutV1>(input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_trie_root() {
|
||||
let mut db = MemoryDB::default();
|
||||
let mut root = TrieHash::<Layout>::default();
|
||||
let mut empty = TrieDBMut::<Layout>::new(&mut db, &mut root);
|
||||
let mut root = TrieHash::<LayoutV1>::default();
|
||||
let mut empty = TrieDBMut::<LayoutV1>::new(&mut db, &mut root);
|
||||
empty.commit();
|
||||
let root1 = empty.root().as_ref().to_vec();
|
||||
let root2: Vec<u8> = Layout::trie_root::<_, Vec<u8>, Vec<u8>>(std::iter::empty())
|
||||
let root2: Vec<u8> = LayoutV1::trie_root::<_, Vec<u8>, Vec<u8>>(std::iter::empty())
|
||||
.as_ref()
|
||||
.iter()
|
||||
.cloned()
|
||||
@@ -525,31 +612,27 @@ mod tests {
|
||||
#[test]
|
||||
fn empty_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![];
|
||||
check_equivalent::<Layout>(&input);
|
||||
check_iteration::<Layout>(&input);
|
||||
check_input(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaf_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![(&[0xaa][..], &[0xbb][..])];
|
||||
check_equivalent::<Layout>(&input);
|
||||
check_iteration::<Layout>(&input);
|
||||
check_input(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> =
|
||||
vec![(&[0xaa][..], &[0x10][..]), (&[0xba][..], &[0x11][..])];
|
||||
check_equivalent::<Layout>(&input);
|
||||
check_iteration::<Layout>(&input);
|
||||
check_input(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_and_branch_is_equivalent() {
|
||||
let input: Vec<(&[u8], &[u8])> =
|
||||
vec![(&[0xaa][..], &[0x10][..]), (&[0xab][..], &[0x11][..])];
|
||||
check_equivalent::<Layout>(&input);
|
||||
check_iteration::<Layout>(&input);
|
||||
check_input(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -564,8 +647,7 @@ mod tests {
|
||||
let mut d = st.make();
|
||||
d.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
|
||||
let dr = d.iter().map(|v| (&v.0[..], &v.1[..])).collect();
|
||||
check_equivalent::<Layout>(&dr);
|
||||
check_iteration::<Layout>(&dr);
|
||||
check_input(&dr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -575,8 +657,7 @@ mod tests {
|
||||
(&[0xaa, 0xaa][..], &[0xaa][..]),
|
||||
(&[0xaa, 0xbb][..], &[0xab][..]),
|
||||
];
|
||||
check_equivalent::<Layout>(&input);
|
||||
check_iteration::<Layout>(&input);
|
||||
check_input(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -589,8 +670,7 @@ mod tests {
|
||||
(&[0xbb, 0xbb][..], &[0xbb][..]),
|
||||
(&[0xbb, 0xcc][..], &[0xbc][..]),
|
||||
];
|
||||
check_equivalent::<Layout>(&input);
|
||||
check_iteration::<Layout>(&input);
|
||||
check_input(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -602,8 +682,7 @@ mod tests {
|
||||
),
|
||||
(&[0xba][..], &[0x11][..]),
|
||||
];
|
||||
check_equivalent::<Layout>(&input);
|
||||
check_iteration::<Layout>(&input);
|
||||
check_input(&input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -618,15 +697,17 @@ mod tests {
|
||||
&b"ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC"[..],
|
||||
),
|
||||
];
|
||||
check_equivalent::<Layout>(&input);
|
||||
check_iteration::<Layout>(&input);
|
||||
check_input(&input);
|
||||
}
|
||||
|
||||
fn populate_trie<'db, T: TrieConfiguration>(
|
||||
fn populate_trie<'db, T>(
|
||||
db: &'db mut dyn HashDB<T::Hash, DBValue>,
|
||||
root: &'db mut TrieHash<T>,
|
||||
v: &[(Vec<u8>, Vec<u8>)],
|
||||
) -> TrieDBMut<'db, T> {
|
||||
) -> TrieDBMut<'db, T>
|
||||
where
|
||||
T: TrieConfiguration,
|
||||
{
|
||||
let mut t = TrieDBMut::<T>::new(db, root);
|
||||
for i in 0..v.len() {
|
||||
let key: &[u8] = &v[i].0;
|
||||
@@ -648,8 +729,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn random_should_work() {
|
||||
random_should_work_inner::<LayoutV1>();
|
||||
random_should_work_inner::<LayoutV0>();
|
||||
}
|
||||
fn random_should_work_inner<L: TrieConfiguration>() {
|
||||
let mut seed = <Blake2Hasher as Hasher>::Out::zero();
|
||||
for test_i in 0..10000 {
|
||||
for test_i in 0..10_000 {
|
||||
if test_i % 50 == 0 {
|
||||
println!("{:?} of 10000 stress tests done", test_i);
|
||||
}
|
||||
@@ -662,10 +747,11 @@ mod tests {
|
||||
}
|
||||
.make_with(seed.as_fixed_bytes_mut());
|
||||
|
||||
let real = Layout::trie_root(x.clone());
|
||||
let real = L::trie_root(x.clone());
|
||||
let mut memdb = MemoryDB::default();
|
||||
let mut root = Default::default();
|
||||
let mut memtrie = populate_trie::<Layout>(&mut memdb, &mut root, &x);
|
||||
|
||||
let mut memtrie = populate_trie::<L>(&mut memdb, &mut root, &x);
|
||||
|
||||
memtrie.commit();
|
||||
if *memtrie.root() != real {
|
||||
@@ -677,9 +763,9 @@ mod tests {
|
||||
}
|
||||
}
|
||||
assert_eq!(*memtrie.root(), real);
|
||||
unpopulate_trie::<Layout>(&mut memtrie, &x);
|
||||
unpopulate_trie::<L>(&mut memtrie, &x);
|
||||
memtrie.commit();
|
||||
let hashed_null_node = hashed_null_node::<Layout>();
|
||||
let hashed_null_node = hashed_null_node::<L>();
|
||||
if *memtrie.root() != hashed_null_node {
|
||||
println!("- TRIE MISMATCH");
|
||||
println!("");
|
||||
@@ -699,7 +785,7 @@ mod tests {
|
||||
#[test]
|
||||
fn codec_trie_empty() {
|
||||
let input: Vec<(&[u8], &[u8])> = vec![];
|
||||
let trie = Layout::trie_root_unhashed::<_, _, _>(input);
|
||||
let trie = LayoutV1::trie_root_unhashed(input);
|
||||
println!("trie: {:#x?}", trie);
|
||||
assert_eq!(trie, vec![0x0]);
|
||||
}
|
||||
@@ -707,7 +793,7 @@ mod tests {
|
||||
#[test]
|
||||
fn codec_trie_single_tuple() {
|
||||
let input = vec![(vec![0xaa], vec![0xbb])];
|
||||
let trie = Layout::trie_root_unhashed::<_, _, _>(input);
|
||||
let trie = LayoutV1::trie_root_unhashed(input);
|
||||
println!("trie: {:#x?}", trie);
|
||||
assert_eq!(
|
||||
trie,
|
||||
@@ -723,7 +809,7 @@ mod tests {
|
||||
#[test]
|
||||
fn codec_trie_two_tuples_disjoint_keys() {
|
||||
let input = vec![(&[0x48, 0x19], &[0xfe]), (&[0x13, 0x14], &[0xff])];
|
||||
let trie = Layout::trie_root_unhashed::<_, _, _>(input);
|
||||
let trie = LayoutV1::trie_root_unhashed(input);
|
||||
println!("trie: {:#x?}", trie);
|
||||
let mut ex = Vec::<u8>::new();
|
||||
ex.push(0x80); // branch, no value (0b_10..) no nibble
|
||||
@@ -747,6 +833,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn iterator_works() {
|
||||
iterator_works_inner::<LayoutV1>();
|
||||
iterator_works_inner::<LayoutV0>();
|
||||
}
|
||||
fn iterator_works_inner<Layout: TrieConfiguration>() {
|
||||
let pairs = vec![
|
||||
(hex!("0103000000000000000464").to_vec(), hex!("0400000000").to_vec()),
|
||||
(hex!("0103000000000000000469").to_vec(), hex!("0401000000").to_vec()),
|
||||
@@ -777,15 +867,15 @@ mod tests {
|
||||
|
||||
let mut memdb = MemoryDB::default();
|
||||
let mut root = Default::default();
|
||||
populate_trie::<Layout>(&mut memdb, &mut root, &pairs);
|
||||
populate_trie::<LayoutV1>(&mut memdb, &mut root, &pairs);
|
||||
|
||||
let non_included_key: Vec<u8> = hex!("0909").to_vec();
|
||||
let proof =
|
||||
generate_trie_proof::<Layout, _, _, _>(&memdb, root, &[non_included_key.clone()])
|
||||
generate_trie_proof::<LayoutV1, _, _, _>(&memdb, root, &[non_included_key.clone()])
|
||||
.unwrap();
|
||||
|
||||
// Verifying that the K was not included into the trie should work.
|
||||
assert!(verify_trie_proof::<Layout, _, _, Vec<u8>>(
|
||||
assert!(verify_trie_proof::<LayoutV1, _, _, Vec<u8>>(
|
||||
&root,
|
||||
&proof,
|
||||
&[(non_included_key.clone(), None)],
|
||||
@@ -793,7 +883,7 @@ mod tests {
|
||||
.is_ok());
|
||||
|
||||
// Verifying that the K was included into the trie should fail.
|
||||
assert!(verify_trie_proof::<Layout, _, _, Vec<u8>>(
|
||||
assert!(verify_trie_proof::<LayoutV1, _, _, Vec<u8>>(
|
||||
&root,
|
||||
&proof,
|
||||
&[(non_included_key, Some(hex!("1010").to_vec()))],
|
||||
@@ -810,13 +900,13 @@ mod tests {
|
||||
|
||||
let mut memdb = MemoryDB::default();
|
||||
let mut root = Default::default();
|
||||
populate_trie::<Layout>(&mut memdb, &mut root, &pairs);
|
||||
populate_trie::<LayoutV1>(&mut memdb, &mut root, &pairs);
|
||||
|
||||
let proof =
|
||||
generate_trie_proof::<Layout, _, _, _>(&memdb, root, &[pairs[0].0.clone()]).unwrap();
|
||||
generate_trie_proof::<LayoutV1, _, _, _>(&memdb, root, &[pairs[0].0.clone()]).unwrap();
|
||||
|
||||
// Check that a K, V included into the proof are verified.
|
||||
assert!(verify_trie_proof::<Layout, _, _, _>(
|
||||
assert!(verify_trie_proof::<LayoutV1, _, _, _>(
|
||||
&root,
|
||||
&proof,
|
||||
&[(pairs[0].0.clone(), Some(pairs[0].1.clone()))]
|
||||
@@ -824,7 +914,7 @@ mod tests {
|
||||
.is_ok());
|
||||
|
||||
// Absence of the V is not verified with the proof that has K, V included.
|
||||
assert!(verify_trie_proof::<Layout, _, _, Vec<u8>>(
|
||||
assert!(verify_trie_proof::<LayoutV1, _, _, Vec<u8>>(
|
||||
&root,
|
||||
&proof,
|
||||
&[(pairs[0].0.clone(), None)]
|
||||
@@ -832,7 +922,7 @@ mod tests {
|
||||
.is_err());
|
||||
|
||||
// K not included into the trie is not verified.
|
||||
assert!(verify_trie_proof::<Layout, _, _, _>(
|
||||
assert!(verify_trie_proof::<LayoutV1, _, _, _>(
|
||||
&root,
|
||||
&proof,
|
||||
&[(hex!("4242").to_vec(), Some(pairs[0].1.clone()))]
|
||||
@@ -840,7 +930,7 @@ mod tests {
|
||||
.is_err());
|
||||
|
||||
// K included into the trie but not included into the proof is not verified.
|
||||
assert!(verify_trie_proof::<Layout, _, _, _>(
|
||||
assert!(verify_trie_proof::<LayoutV1, _, _, _>(
|
||||
&root,
|
||||
&proof,
|
||||
&[(pairs[1].0.clone(), Some(pairs[1].1.clone()))]
|
||||
@@ -865,13 +955,13 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let proof_db = proof.into_memory_db::<Blake2Hasher>();
|
||||
let first_storage_root = delta_trie_root::<Layout, _, _, _, _, _>(
|
||||
let first_storage_root = delta_trie_root::<LayoutV0, _, _, _, _, _>(
|
||||
&mut proof_db.clone(),
|
||||
storage_root,
|
||||
valid_delta,
|
||||
)
|
||||
.unwrap();
|
||||
let second_storage_root = delta_trie_root::<Layout, _, _, _, _, _>(
|
||||
let second_storage_root = delta_trie_root::<LayoutV0, _, _, _, _, _>(
|
||||
&mut proof_db.clone(),
|
||||
storage_root,
|
||||
invalid_delta,
|
||||
|
||||
@@ -24,7 +24,7 @@ use hash_db::Hasher;
|
||||
use sp_std::{borrow::Borrow, marker::PhantomData, ops::Range, vec::Vec};
|
||||
use trie_db::{
|
||||
self, nibble_ops,
|
||||
node::{NibbleSlicePlan, NodeHandlePlan, NodePlan},
|
||||
node::{NibbleSlicePlan, NodeHandlePlan, NodePlan, Value, ValuePlan},
|
||||
ChildReference, NodeCodec as NodeCodecT, Partial,
|
||||
};
|
||||
|
||||
@@ -80,7 +80,11 @@ impl<'a> Input for ByteSliceInput<'a> {
|
||||
#[derive(Default, Clone)]
|
||||
pub struct NodeCodec<H>(PhantomData<H>);
|
||||
|
||||
impl<H: Hasher> NodeCodecT for NodeCodec<H> {
|
||||
impl<H> NodeCodecT for NodeCodec<H>
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
const ESCAPE_HEADER: Option<u8> = Some(trie_constants::ESCAPE_COMPACT_HEADER);
|
||||
type Error = Error;
|
||||
type HashOut = H::Out;
|
||||
|
||||
@@ -88,11 +92,22 @@ impl<H: Hasher> NodeCodecT for NodeCodec<H> {
|
||||
H::hash(<Self as NodeCodecT>::empty_node())
|
||||
}
|
||||
|
||||
fn decode_plan(data: &[u8]) -> sp_std::result::Result<NodePlan, Self::Error> {
|
||||
fn decode_plan(data: &[u8]) -> Result<NodePlan, Self::Error> {
|
||||
let mut input = ByteSliceInput::new(data);
|
||||
match NodeHeader::decode(&mut input)? {
|
||||
|
||||
let header = NodeHeader::decode(&mut input)?;
|
||||
let contains_hash = header.contains_hash_of_value();
|
||||
|
||||
let branch_has_value = if let NodeHeader::Branch(has_value, _) = &header {
|
||||
*has_value
|
||||
} else {
|
||||
// hashed_value_branch
|
||||
true
|
||||
};
|
||||
|
||||
match header {
|
||||
NodeHeader::Null => Ok(NodePlan::Empty),
|
||||
NodeHeader::Branch(has_value, nibble_count) => {
|
||||
NodeHeader::HashedValueBranch(nibble_count) | NodeHeader::Branch(_, nibble_count) => {
|
||||
let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0;
|
||||
// check that the padding is valid (if any)
|
||||
if padding && nibble_ops::pad_left(data[input.offset]) != 0 {
|
||||
@@ -105,9 +120,13 @@ impl<H: Hasher> NodeCodecT for NodeCodec<H> {
|
||||
let partial_padding = nibble_ops::number_padding(nibble_count);
|
||||
let bitmap_range = input.take(BITMAP_LENGTH)?;
|
||||
let bitmap = Bitmap::decode(&data[bitmap_range])?;
|
||||
let value = if has_value {
|
||||
let count = <Compact<u32>>::decode(&mut input)?.0 as usize;
|
||||
Some(input.take(count)?)
|
||||
let value = if branch_has_value {
|
||||
Some(if contains_hash {
|
||||
ValuePlan::Node(input.take(H::LENGTH)?)
|
||||
} else {
|
||||
let count = <Compact<u32>>::decode(&mut input)?.0 as usize;
|
||||
ValuePlan::Inline(input.take(count)?)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -132,7 +151,7 @@ impl<H: Hasher> NodeCodecT for NodeCodec<H> {
|
||||
children,
|
||||
})
|
||||
},
|
||||
NodeHeader::Leaf(nibble_count) => {
|
||||
NodeHeader::HashedValueLeaf(nibble_count) | NodeHeader::Leaf(nibble_count) => {
|
||||
let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0;
|
||||
// check that the padding is valid (if any)
|
||||
if padding && nibble_ops::pad_left(data[input.offset]) != 0 {
|
||||
@@ -143,10 +162,16 @@ impl<H: Hasher> NodeCodecT for NodeCodec<H> {
|
||||
nibble_ops::NIBBLE_PER_BYTE,
|
||||
)?;
|
||||
let partial_padding = nibble_ops::number_padding(nibble_count);
|
||||
let count = <Compact<u32>>::decode(&mut input)?.0 as usize;
|
||||
let value = if contains_hash {
|
||||
ValuePlan::Node(input.take(H::LENGTH)?)
|
||||
} else {
|
||||
let count = <Compact<u32>>::decode(&mut input)?.0 as usize;
|
||||
ValuePlan::Inline(input.take(count)?)
|
||||
};
|
||||
|
||||
Ok(NodePlan::Leaf {
|
||||
partial: NibbleSlicePlan::new(partial, partial_padding),
|
||||
value: input.take(count)?,
|
||||
value,
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -160,9 +185,23 @@ impl<H: Hasher> NodeCodecT for NodeCodec<H> {
|
||||
&[trie_constants::EMPTY_TRIE]
|
||||
}
|
||||
|
||||
fn leaf_node(partial: Partial, value: &[u8]) -> Vec<u8> {
|
||||
let mut output = partial_encode(partial, NodeKind::Leaf);
|
||||
value.encode_to(&mut output);
|
||||
fn leaf_node(partial: Partial, value: Value) -> Vec<u8> {
|
||||
let contains_hash = matches!(&value, Value::Node(..));
|
||||
let mut output = if contains_hash {
|
||||
partial_encode(partial, NodeKind::HashedValueLeaf)
|
||||
} else {
|
||||
partial_encode(partial, NodeKind::Leaf)
|
||||
};
|
||||
match value {
|
||||
Value::Inline(value) => {
|
||||
Compact(value.len() as u32).encode_to(&mut output);
|
||||
output.extend_from_slice(value);
|
||||
},
|
||||
Value::Node(hash, _) => {
|
||||
debug_assert!(hash.len() == H::LENGTH);
|
||||
output.extend_from_slice(hash);
|
||||
},
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
@@ -171,33 +210,46 @@ impl<H: Hasher> NodeCodecT for NodeCodec<H> {
|
||||
_nbnibble: usize,
|
||||
_child: ChildReference<<H as Hasher>::Out>,
|
||||
) -> Vec<u8> {
|
||||
unreachable!()
|
||||
unreachable!("No extension codec.")
|
||||
}
|
||||
|
||||
fn branch_node(
|
||||
_children: impl Iterator<Item = impl Borrow<Option<ChildReference<<H as Hasher>::Out>>>>,
|
||||
_maybe_value: Option<&[u8]>,
|
||||
_maybe_value: Option<Value>,
|
||||
) -> Vec<u8> {
|
||||
unreachable!()
|
||||
unreachable!("No extension codec.")
|
||||
}
|
||||
|
||||
fn branch_node_nibbled(
|
||||
partial: impl Iterator<Item = u8>,
|
||||
number_nibble: usize,
|
||||
children: impl Iterator<Item = impl Borrow<Option<ChildReference<<H as Hasher>::Out>>>>,
|
||||
maybe_value: Option<&[u8]>,
|
||||
value: Option<Value>,
|
||||
) -> Vec<u8> {
|
||||
let mut output = if maybe_value.is_some() {
|
||||
partial_from_iterator_encode(partial, number_nibble, NodeKind::BranchWithValue)
|
||||
} else {
|
||||
partial_from_iterator_encode(partial, number_nibble, NodeKind::BranchNoValue)
|
||||
let contains_hash = matches!(&value, Some(Value::Node(..)));
|
||||
let mut output = match (&value, contains_hash) {
|
||||
(&None, _) =>
|
||||
partial_from_iterator_encode(partial, number_nibble, NodeKind::BranchNoValue),
|
||||
(_, false) =>
|
||||
partial_from_iterator_encode(partial, number_nibble, NodeKind::BranchWithValue),
|
||||
(_, true) =>
|
||||
partial_from_iterator_encode(partial, number_nibble, NodeKind::HashedValueBranch),
|
||||
};
|
||||
|
||||
let bitmap_index = output.len();
|
||||
let mut bitmap: [u8; BITMAP_LENGTH] = [0; BITMAP_LENGTH];
|
||||
(0..BITMAP_LENGTH).for_each(|_| output.push(0));
|
||||
if let Some(value) = maybe_value {
|
||||
value.encode_to(&mut output);
|
||||
};
|
||||
match value {
|
||||
Some(Value::Inline(value)) => {
|
||||
Compact(value.len() as u32).encode_to(&mut output);
|
||||
output.extend_from_slice(value);
|
||||
},
|
||||
Some(Value::Node(hash, _)) => {
|
||||
debug_assert!(hash.len() == H::LENGTH);
|
||||
output.extend_from_slice(hash);
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
Bitmap::encode(
|
||||
children.map(|maybe_child| match maybe_child.borrow() {
|
||||
Some(ChildReference::Hash(h)) => {
|
||||
@@ -229,11 +281,15 @@ fn partial_from_iterator_encode<I: Iterator<Item = u8>>(
|
||||
) -> Vec<u8> {
|
||||
let nibble_count = sp_std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, nibble_count);
|
||||
|
||||
let mut output = Vec::with_capacity(3 + (nibble_count / nibble_ops::NIBBLE_PER_BYTE));
|
||||
let mut output = Vec::with_capacity(4 + (nibble_count / nibble_ops::NIBBLE_PER_BYTE));
|
||||
match node_kind {
|
||||
NodeKind::Leaf => NodeHeader::Leaf(nibble_count).encode_to(&mut output),
|
||||
NodeKind::BranchWithValue => NodeHeader::Branch(true, nibble_count).encode_to(&mut output),
|
||||
NodeKind::BranchNoValue => NodeHeader::Branch(false, nibble_count).encode_to(&mut output),
|
||||
NodeKind::HashedValueLeaf =>
|
||||
NodeHeader::HashedValueLeaf(nibble_count).encode_to(&mut output),
|
||||
NodeKind::HashedValueBranch =>
|
||||
NodeHeader::HashedValueBranch(nibble_count).encode_to(&mut output),
|
||||
};
|
||||
output.extend(partial);
|
||||
output
|
||||
@@ -247,11 +303,15 @@ fn partial_encode(partial: Partial, node_kind: NodeKind) -> Vec<u8> {
|
||||
|
||||
let nibble_count = sp_std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, nibble_count);
|
||||
|
||||
let mut output = Vec::with_capacity(3 + partial.1.len());
|
||||
let mut output = Vec::with_capacity(4 + partial.1.len());
|
||||
match node_kind {
|
||||
NodeKind::Leaf => NodeHeader::Leaf(nibble_count).encode_to(&mut output),
|
||||
NodeKind::BranchWithValue => NodeHeader::Branch(true, nibble_count).encode_to(&mut output),
|
||||
NodeKind::BranchNoValue => NodeHeader::Branch(false, nibble_count).encode_to(&mut output),
|
||||
NodeKind::HashedValueLeaf =>
|
||||
NodeHeader::HashedValueLeaf(nibble_count).encode_to(&mut output),
|
||||
NodeKind::HashedValueBranch =>
|
||||
NodeHeader::HashedValueBranch(nibble_count).encode_to(&mut output),
|
||||
};
|
||||
if number_nibble_encoded > 0 {
|
||||
output.push(nibble_ops::pad_right((partial.0).1));
|
||||
|
||||
@@ -25,8 +25,20 @@ use sp_std::iter::once;
|
||||
#[derive(Copy, Clone, PartialEq, Eq, sp_core::RuntimeDebug)]
|
||||
pub(crate) enum NodeHeader {
|
||||
Null,
|
||||
// contains wether there is a value and nibble count
|
||||
Branch(bool, usize),
|
||||
// contains nibble count
|
||||
Leaf(usize),
|
||||
// contains nibble count.
|
||||
HashedValueBranch(usize),
|
||||
// contains nibble count.
|
||||
HashedValueLeaf(usize),
|
||||
}
|
||||
|
||||
impl NodeHeader {
|
||||
pub(crate) fn contains_hash_of_value(&self) -> bool {
|
||||
matches!(self, NodeHeader::HashedValueBranch(_) | NodeHeader::HashedValueLeaf(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// NodeHeader without content
|
||||
@@ -34,6 +46,8 @@ pub(crate) enum NodeKind {
|
||||
Leaf,
|
||||
BranchNoValue,
|
||||
BranchWithValue,
|
||||
HashedValueLeaf,
|
||||
HashedValueBranch,
|
||||
}
|
||||
|
||||
impl Encode for NodeHeader {
|
||||
@@ -41,11 +55,27 @@ impl Encode for NodeHeader {
|
||||
match self {
|
||||
NodeHeader::Null => output.push_byte(trie_constants::EMPTY_TRIE),
|
||||
NodeHeader::Branch(true, nibble_count) =>
|
||||
encode_size_and_prefix(*nibble_count, trie_constants::BRANCH_WITH_MASK, output),
|
||||
NodeHeader::Branch(false, nibble_count) =>
|
||||
encode_size_and_prefix(*nibble_count, trie_constants::BRANCH_WITHOUT_MASK, output),
|
||||
encode_size_and_prefix(*nibble_count, trie_constants::BRANCH_WITH_MASK, 2, output),
|
||||
NodeHeader::Branch(false, nibble_count) => encode_size_and_prefix(
|
||||
*nibble_count,
|
||||
trie_constants::BRANCH_WITHOUT_MASK,
|
||||
2,
|
||||
output,
|
||||
),
|
||||
NodeHeader::Leaf(nibble_count) =>
|
||||
encode_size_and_prefix(*nibble_count, trie_constants::LEAF_PREFIX_MASK, output),
|
||||
encode_size_and_prefix(*nibble_count, trie_constants::LEAF_PREFIX_MASK, 2, output),
|
||||
NodeHeader::HashedValueBranch(nibble_count) => encode_size_and_prefix(
|
||||
*nibble_count,
|
||||
trie_constants::ALT_HASHING_BRANCH_WITH_MASK,
|
||||
4,
|
||||
output,
|
||||
),
|
||||
NodeHeader::HashedValueLeaf(nibble_count) => encode_size_and_prefix(
|
||||
*nibble_count,
|
||||
trie_constants::ALT_HASHING_LEAF_PREFIX_MASK,
|
||||
3,
|
||||
output,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,13 +89,22 @@ impl Decode for NodeHeader {
|
||||
return Ok(NodeHeader::Null)
|
||||
}
|
||||
match i & (0b11 << 6) {
|
||||
trie_constants::LEAF_PREFIX_MASK => Ok(NodeHeader::Leaf(decode_size(i, input)?)),
|
||||
trie_constants::BRANCH_WITHOUT_MASK =>
|
||||
Ok(NodeHeader::Branch(false, decode_size(i, input)?)),
|
||||
trie_constants::LEAF_PREFIX_MASK => Ok(NodeHeader::Leaf(decode_size(i, input, 2)?)),
|
||||
trie_constants::BRANCH_WITH_MASK =>
|
||||
Ok(NodeHeader::Branch(true, decode_size(i, input)?)),
|
||||
// do not allow any special encoding
|
||||
_ => Err("Unallowed encoding".into()),
|
||||
Ok(NodeHeader::Branch(true, decode_size(i, input, 2)?)),
|
||||
trie_constants::BRANCH_WITHOUT_MASK =>
|
||||
Ok(NodeHeader::Branch(false, decode_size(i, input, 2)?)),
|
||||
trie_constants::EMPTY_TRIE => {
|
||||
if i & (0b111 << 5) == trie_constants::ALT_HASHING_LEAF_PREFIX_MASK {
|
||||
Ok(NodeHeader::HashedValueLeaf(decode_size(i, input, 3)?))
|
||||
} else if i & (0b1111 << 4) == trie_constants::ALT_HASHING_BRANCH_WITH_MASK {
|
||||
Ok(NodeHeader::HashedValueBranch(decode_size(i, input, 4)?))
|
||||
} else {
|
||||
// do not allow any special encoding
|
||||
Err("Unallowed encoding".into())
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,12 +112,20 @@ impl Decode for NodeHeader {
|
||||
/// Returns an iterator over encoded bytes for node header and size.
|
||||
/// Size encoding allows unlimited, length inefficient, representation, but
|
||||
/// is bounded to 16 bit maximum value to avoid possible DOS.
|
||||
pub(crate) fn size_and_prefix_iterator(size: usize, prefix: u8) -> impl Iterator<Item = u8> {
|
||||
pub(crate) fn size_and_prefix_iterator(
|
||||
size: usize,
|
||||
prefix: u8,
|
||||
prefix_mask: usize,
|
||||
) -> impl Iterator<Item = u8> {
|
||||
let size = sp_std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, size);
|
||||
|
||||
let l1 = sp_std::cmp::min(62, size);
|
||||
let (first_byte, mut rem) =
|
||||
if size == l1 { (once(prefix + l1 as u8), 0) } else { (once(prefix + 63), size - l1) };
|
||||
let max_value = 255u8 >> prefix_mask;
|
||||
let l1 = sp_std::cmp::min((max_value as usize).saturating_sub(1), size);
|
||||
let (first_byte, mut rem) = if size == l1 {
|
||||
(once(prefix + l1 as u8), 0)
|
||||
} else {
|
||||
(once(prefix + max_value as u8), size - l1)
|
||||
};
|
||||
let next_bytes = move || {
|
||||
if rem > 0 {
|
||||
if rem < 256 {
|
||||
@@ -97,16 +144,24 @@ pub(crate) fn size_and_prefix_iterator(size: usize, prefix: u8) -> impl Iterator
|
||||
}
|
||||
|
||||
/// Encodes size and prefix to a stream output.
|
||||
fn encode_size_and_prefix<W: Output + ?Sized>(size: usize, prefix: u8, out: &mut W) {
|
||||
for b in size_and_prefix_iterator(size, prefix) {
|
||||
fn encode_size_and_prefix<W>(size: usize, prefix: u8, prefix_mask: usize, out: &mut W)
|
||||
where
|
||||
W: Output + ?Sized,
|
||||
{
|
||||
for b in size_and_prefix_iterator(size, prefix, prefix_mask) {
|
||||
out.push_byte(b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Decode size only from stream input and header byte.
|
||||
fn decode_size(first: u8, input: &mut impl Input) -> Result<usize, codec::Error> {
|
||||
let mut result = (first & 255u8 >> 2) as usize;
|
||||
if result < 63 {
|
||||
fn decode_size(
|
||||
first: u8,
|
||||
input: &mut impl Input,
|
||||
prefix_mask: usize,
|
||||
) -> Result<usize, codec::Error> {
|
||||
let max_value = 255u8 >> prefix_mask;
|
||||
let mut result = (first & max_value) as usize;
|
||||
if result < max_value as usize {
|
||||
return Ok(result)
|
||||
}
|
||||
result -= 1;
|
||||
|
||||
@@ -19,6 +19,9 @@ use codec::{Decode, Encode};
|
||||
use hash_db::{HashDB, Hasher};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_std::vec::Vec;
|
||||
// Note that `LayoutV1` usage here (proof compaction) is compatible
|
||||
// with `LayoutV0`.
|
||||
use crate::LayoutV1 as Layout;
|
||||
|
||||
/// A proof that some set of key-value pairs are included in the storage trie. The proof contains
|
||||
/// the storage values so that the partial storage backend can be reconstructed by a verifier that
|
||||
@@ -95,8 +98,8 @@ impl StorageProof {
|
||||
pub fn into_compact_proof<H: Hasher>(
|
||||
self,
|
||||
root: H::Out,
|
||||
) -> Result<CompactProof, crate::CompactProofError<crate::Layout<H>>> {
|
||||
crate::encode_compact::<crate::Layout<H>>(self, root)
|
||||
) -> Result<CompactProof, crate::CompactProofError<Layout<H>>> {
|
||||
crate::encode_compact::<Layout<H>>(self, root)
|
||||
}
|
||||
|
||||
/// Returns the estimated encoded size of the compact proof.
|
||||
@@ -124,9 +127,9 @@ impl CompactProof {
|
||||
pub fn to_storage_proof<H: Hasher>(
|
||||
&self,
|
||||
expected_root: Option<&H::Out>,
|
||||
) -> Result<(StorageProof, H::Out), crate::CompactProofError<crate::Layout<H>>> {
|
||||
) -> Result<(StorageProof, H::Out), crate::CompactProofError<Layout<H>>> {
|
||||
let mut db = crate::MemoryDB::<H>::new(&[]);
|
||||
let root = crate::decode_compact::<crate::Layout<H>, _, _>(
|
||||
let root = crate::decode_compact::<Layout<H>, _, _>(
|
||||
&mut db,
|
||||
self.iter_compact_encoded_nodes(),
|
||||
expected_root,
|
||||
|
||||
@@ -112,8 +112,7 @@ where
|
||||
I: IntoIterator<Item = &'a [u8]>,
|
||||
{
|
||||
let mut nodes_iter = encoded.into_iter();
|
||||
let (top_root, _nb_used) =
|
||||
trie_db::decode_compact_from_iter::<L, _, _, _>(db, &mut nodes_iter)?;
|
||||
let (top_root, _nb_used) = trie_db::decode_compact_from_iter::<L, _, _>(db, &mut nodes_iter)?;
|
||||
|
||||
// Only check root if expected root is passed as argument.
|
||||
if let Some(expected_root) = expected_root {
|
||||
@@ -164,8 +163,7 @@ where
|
||||
let mut nodes_iter = nodes_iter.peekable();
|
||||
for child_root in child_tries.into_iter() {
|
||||
if previous_extracted_child_trie.is_none() && nodes_iter.peek().is_some() {
|
||||
let (top_root, _) =
|
||||
trie_db::decode_compact_from_iter::<L, _, _, _>(db, &mut nodes_iter)?;
|
||||
let (top_root, _) = trie_db::decode_compact_from_iter::<L, _, _>(db, &mut nodes_iter)?;
|
||||
previous_extracted_child_trie = Some(top_root);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,21 +18,18 @@
|
||||
//! `TrieStream` implementation for Substrate's trie format.
|
||||
|
||||
use crate::{
|
||||
node_codec::Bitmap,
|
||||
node_header::{size_and_prefix_iterator, NodeKind},
|
||||
trie_constants,
|
||||
};
|
||||
use codec::Encode;
|
||||
use codec::{Compact, Encode};
|
||||
use hash_db::Hasher;
|
||||
use sp_std::vec::Vec;
|
||||
use trie_root;
|
||||
|
||||
const BRANCH_NODE_NO_VALUE: u8 = 254;
|
||||
const BRANCH_NODE_WITH_VALUE: u8 = 255;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
/// Codec-flavored TrieStream.
|
||||
pub struct TrieStream {
|
||||
/// Current node buffer.
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
@@ -60,51 +57,76 @@ fn fuse_nibbles_node<'a>(nibbles: &'a [u8], kind: NodeKind) -> impl Iterator<Ite
|
||||
let size = sp_std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, nibbles.len());
|
||||
|
||||
let iter_start = match kind {
|
||||
NodeKind::Leaf => size_and_prefix_iterator(size, trie_constants::LEAF_PREFIX_MASK),
|
||||
NodeKind::Leaf => size_and_prefix_iterator(size, trie_constants::LEAF_PREFIX_MASK, 2),
|
||||
NodeKind::BranchNoValue =>
|
||||
size_and_prefix_iterator(size, trie_constants::BRANCH_WITHOUT_MASK),
|
||||
size_and_prefix_iterator(size, trie_constants::BRANCH_WITHOUT_MASK, 2),
|
||||
NodeKind::BranchWithValue =>
|
||||
size_and_prefix_iterator(size, trie_constants::BRANCH_WITH_MASK),
|
||||
size_and_prefix_iterator(size, trie_constants::BRANCH_WITH_MASK, 2),
|
||||
NodeKind::HashedValueLeaf =>
|
||||
size_and_prefix_iterator(size, trie_constants::ALT_HASHING_LEAF_PREFIX_MASK, 3),
|
||||
NodeKind::HashedValueBranch =>
|
||||
size_and_prefix_iterator(size, trie_constants::ALT_HASHING_BRANCH_WITH_MASK, 4),
|
||||
};
|
||||
iter_start
|
||||
.chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None })
|
||||
.chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1]))
|
||||
}
|
||||
|
||||
use trie_root::Value as TrieStreamValue;
|
||||
impl trie_root::TrieStream for TrieStream {
|
||||
fn new() -> Self {
|
||||
TrieStream { buffer: Vec::new() }
|
||||
Self { buffer: Vec::new() }
|
||||
}
|
||||
|
||||
fn append_empty_data(&mut self) {
|
||||
self.buffer.push(trie_constants::EMPTY_TRIE);
|
||||
}
|
||||
|
||||
fn append_leaf(&mut self, key: &[u8], value: &[u8]) {
|
||||
self.buffer.extend(fuse_nibbles_node(key, NodeKind::Leaf));
|
||||
value.encode_to(&mut self.buffer);
|
||||
fn append_leaf(&mut self, key: &[u8], value: TrieStreamValue) {
|
||||
let kind = match &value {
|
||||
TrieStreamValue::Inline(..) => NodeKind::Leaf,
|
||||
TrieStreamValue::Node(..) => NodeKind::HashedValueLeaf,
|
||||
};
|
||||
self.buffer.extend(fuse_nibbles_node(key, kind));
|
||||
match &value {
|
||||
TrieStreamValue::Inline(value) => {
|
||||
Compact(value.len() as u32).encode_to(&mut self.buffer);
|
||||
self.buffer.extend_from_slice(value);
|
||||
},
|
||||
TrieStreamValue::Node(hash) => {
|
||||
self.buffer.extend_from_slice(hash.as_slice());
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn begin_branch(
|
||||
&mut self,
|
||||
maybe_partial: Option<&[u8]>,
|
||||
maybe_value: Option<&[u8]>,
|
||||
maybe_value: Option<TrieStreamValue>,
|
||||
has_children: impl Iterator<Item = bool>,
|
||||
) {
|
||||
if let Some(partial) = maybe_partial {
|
||||
if maybe_value.is_some() {
|
||||
self.buffer.extend(fuse_nibbles_node(partial, NodeKind::BranchWithValue));
|
||||
} else {
|
||||
self.buffer.extend(fuse_nibbles_node(partial, NodeKind::BranchNoValue));
|
||||
}
|
||||
let kind = match &maybe_value {
|
||||
None => NodeKind::BranchNoValue,
|
||||
Some(TrieStreamValue::Inline(..)) => NodeKind::BranchWithValue,
|
||||
Some(TrieStreamValue::Node(..)) => NodeKind::HashedValueBranch,
|
||||
};
|
||||
|
||||
self.buffer.extend(fuse_nibbles_node(partial, kind));
|
||||
let bm = branch_node_bit_mask(has_children);
|
||||
self.buffer.extend([bm.0, bm.1].iter());
|
||||
} else {
|
||||
debug_assert!(false, "trie stream codec only for no extension trie");
|
||||
self.buffer.extend(&branch_node(maybe_value.is_some(), has_children));
|
||||
unreachable!("trie stream codec only for no extension trie");
|
||||
}
|
||||
if let Some(value) = maybe_value {
|
||||
value.encode_to(&mut self.buffer);
|
||||
match maybe_value {
|
||||
None => (),
|
||||
Some(TrieStreamValue::Inline(value)) => {
|
||||
Compact(value.len() as u32).encode_to(&mut self.buffer);
|
||||
self.buffer.extend_from_slice(value);
|
||||
},
|
||||
Some(TrieStreamValue::Node(hash)) => {
|
||||
self.buffer.extend_from_slice(hash.as_slice());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,18 +146,3 @@ impl trie_root::TrieStream for TrieStream {
|
||||
self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_node(has_value: bool, has_children: impl Iterator<Item = bool>) -> [u8; 3] {
|
||||
let mut result = [0, 0, 0];
|
||||
branch_node_buffered(has_value, has_children, &mut result[..]);
|
||||
result
|
||||
}
|
||||
|
||||
fn branch_node_buffered<I>(has_value: bool, has_children: I, output: &mut [u8])
|
||||
where
|
||||
I: Iterator<Item = bool>,
|
||||
{
|
||||
let first = if has_value { BRANCH_NODE_WITH_VALUE } else { BRANCH_NODE_NO_VALUE };
|
||||
output[0] = first;
|
||||
Bitmap::encode(has_children, &mut output[1..]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user