mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-15 02:11: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:
Generated
+23
-11
@@ -4179,6 +4179,17 @@ dependencies = [
|
||||
"parity-util-mem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memory-db"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d505169b746dacf02f7d14d8c80b34edfd8212159c63d23c977739a0d960c626"
|
||||
dependencies = [
|
||||
"hash-db",
|
||||
"hashbrown 0.11.2",
|
||||
"parity-util-mem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memory_units"
|
||||
version = "0.3.0"
|
||||
@@ -9908,7 +9919,7 @@ dependencies = [
|
||||
"criterion",
|
||||
"hash-db",
|
||||
"hex-literal",
|
||||
"memory-db",
|
||||
"memory-db 0.28.0",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core",
|
||||
@@ -9929,6 +9940,7 @@ dependencies = [
|
||||
"parity-wasm 0.42.2",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"sp-core-hashing-proc-macro",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"sp-version-proc-macro",
|
||||
@@ -10201,7 +10213,7 @@ dependencies = [
|
||||
"frame-system-rpc-runtime-api",
|
||||
"futures 0.3.16",
|
||||
"log 0.4.14",
|
||||
"memory-db",
|
||||
"memory-db 0.27.0",
|
||||
"pallet-babe",
|
||||
"pallet-timestamp",
|
||||
"parity-scale-codec",
|
||||
@@ -10840,14 +10852,14 @@ checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
|
||||
|
||||
[[package]]
|
||||
name = "trie-bench"
|
||||
version = "0.28.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4edd9bdf0c2e08fd77c0fb2608179cac7ebed997ae18f58d47a2d96425ff51f0"
|
||||
checksum = "36ac46f6503d0fa976193db46f9dbb1d454e5dbde76495f1316f576c7f3f0e6b"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"hash-db",
|
||||
"keccak-hasher",
|
||||
"memory-db",
|
||||
"memory-db 0.28.0",
|
||||
"parity-scale-codec",
|
||||
"trie-db",
|
||||
"trie-root",
|
||||
@@ -10856,9 +10868,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "trie-db"
|
||||
version = "0.22.6"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9eac131e334e81b6b3be07399482042838adcd7957aa0010231d0813e39e02fa"
|
||||
checksum = "e3ddae50680c12ef75bfbf58416ca6622fa43d879553f6cb2ed1a817346e1ffe"
|
||||
dependencies = [
|
||||
"hash-db",
|
||||
"hashbrown 0.11.2",
|
||||
@@ -10869,9 +10881,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "trie-root"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "652931506d2c1244d7217a70b99f56718a7b4161b37f04e7cd868072a99f68cd"
|
||||
checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891"
|
||||
dependencies = [
|
||||
"hash-db",
|
||||
]
|
||||
@@ -10986,8 +10998,8 @@ version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f559b464de2e2bdabcac6a210d12e9b5a5973c251e102c44c585c71d51bd78e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"rand 0.8.4",
|
||||
"cfg-if 0.1.10",
|
||||
"rand 0.6.5",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
impl_version: 1,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
state_version: 1,
|
||||
};
|
||||
|
||||
/// This determines the average expected block time that we are targeting.
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use kvdb::KeyValueDB;
|
||||
use node_primitives::Hash;
|
||||
use sp_trie::{trie_types::TrieDBMut, TrieMut};
|
||||
use sp_trie::{trie_types::TrieDBMutV1, TrieMut};
|
||||
|
||||
use crate::simple_trie::SimpleTrie;
|
||||
|
||||
@@ -43,8 +43,7 @@ pub fn generate_trie(
|
||||
);
|
||||
let mut trie = SimpleTrie { db, overlay: &mut overlay };
|
||||
{
|
||||
let mut trie_db = TrieDBMut::new(&mut trie, &mut root);
|
||||
|
||||
let mut trie_db = TrieDBMutV1::<crate::simple_trie::Hasher>::new(&mut trie, &mut root);
|
||||
for (key, value) in key_values {
|
||||
trie_db.insert(&key, &value).expect("trie insertion failed");
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use kvdb::KeyValueDB;
|
||||
use lazy_static::lazy_static;
|
||||
use rand::Rng;
|
||||
use sp_state_machine::Backend as _;
|
||||
use sp_trie::{trie_types::TrieDBMut, TrieMut as _};
|
||||
use sp_trie::{trie_types::TrieDBMutV1, TrieMut as _};
|
||||
use std::{borrow::Cow, collections::HashMap, sync::Arc};
|
||||
|
||||
use node_primitives::Hash;
|
||||
@@ -286,8 +286,8 @@ impl core::Benchmark for TrieWriteBenchmark {
|
||||
|
||||
let mut overlay = HashMap::new();
|
||||
let mut trie = SimpleTrie { db: kvdb.clone(), overlay: &mut overlay };
|
||||
let mut trie_db_mut =
|
||||
TrieDBMut::from_existing(&mut trie, &mut new_root).expect("Failed to create TrieDBMut");
|
||||
let mut trie_db_mut = TrieDBMutV1::from_existing(&mut trie, &mut new_root)
|
||||
.expect("Failed to create TrieDBMut");
|
||||
|
||||
for (warmup_key, warmup_value) in self.warmup_keys.iter() {
|
||||
let value = trie_db_mut
|
||||
|
||||
@@ -83,14 +83,14 @@ fn construct_block<E: Externalities>(
|
||||
parent_hash: Hash,
|
||||
extrinsics: Vec<CheckedExtrinsic>,
|
||||
) -> (Vec<u8>, Hash) {
|
||||
use sp_trie::{trie_types::Layout, TrieConfiguration};
|
||||
use sp_trie::{LayoutV0, TrieConfiguration};
|
||||
|
||||
// sign extrinsics.
|
||||
let extrinsics = extrinsics.into_iter().map(sign).collect::<Vec<_>>();
|
||||
|
||||
// calculate the header fields that we can.
|
||||
let extrinsics_root =
|
||||
Layout::<BlakeTwo256>::ordered_trie_root(extrinsics.iter().map(Encode::encode))
|
||||
LayoutV0::<BlakeTwo256>::ordered_trie_root(extrinsics.iter().map(Encode::encode))
|
||||
.to_fixed_bytes()
|
||||
.into();
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ pub fn construct_block(
|
||||
extrinsics: Vec<CheckedExtrinsic>,
|
||||
babe_slot: Slot,
|
||||
) -> (Vec<u8>, Hash) {
|
||||
use sp_trie::{trie_types::Layout, TrieConfiguration};
|
||||
use sp_trie::{LayoutV1 as Layout, TrieConfiguration};
|
||||
|
||||
// sign extrinsics.
|
||||
let extrinsics = extrinsics.into_iter().map(sign).collect::<Vec<_>>();
|
||||
|
||||
@@ -126,6 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
impl_version: 0,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 2,
|
||||
state_version: 1,
|
||||
};
|
||||
|
||||
/// The BABE epoch configuration at genesis.
|
||||
|
||||
@@ -29,7 +29,7 @@ use sp_core::offchain::OffchainStorage;
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, HashFor, NumberFor},
|
||||
Justification, Justifications, Storage,
|
||||
Justification, Justifications, StateVersion, Storage,
|
||||
};
|
||||
use sp_state_machine::{
|
||||
ChildStorageCollection, IndexOperation, OffchainChangesCollection, StorageCollection,
|
||||
@@ -166,10 +166,15 @@ pub trait BlockImportOperation<Block: BlockT> {
|
||||
&mut self,
|
||||
storage: Storage,
|
||||
commit: bool,
|
||||
state_version: StateVersion,
|
||||
) -> sp_blockchain::Result<Block::Hash>;
|
||||
|
||||
/// Inject storage data into the database replacing any existing data.
|
||||
fn reset_storage(&mut self, storage: Storage) -> sp_blockchain::Result<Block::Hash>;
|
||||
fn reset_storage(
|
||||
&mut self,
|
||||
storage: Storage,
|
||||
state_version: StateVersion,
|
||||
) -> sp_blockchain::Result<Block::Hash>;
|
||||
|
||||
/// Set storage changes.
|
||||
fn update_storage(
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//! A method call executor interface.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use sc_executor::RuntimeVersion;
|
||||
use sc_executor::{RuntimeVersion, RuntimeVersionOf};
|
||||
use sp_core::NativeOrEncoded;
|
||||
use sp_externalities::Extensions;
|
||||
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
||||
@@ -42,7 +42,7 @@ pub trait ExecutorProvider<Block: BlockT> {
|
||||
}
|
||||
|
||||
/// Method call executor.
|
||||
pub trait CallExecutor<B: BlockT> {
|
||||
pub trait CallExecutor<B: BlockT>: RuntimeVersionOf {
|
||||
/// Externalities error type.
|
||||
type Error: sp_state_machine::Error;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ use sp_core::{
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, HashFor, Header as HeaderT, NumberFor, Zero},
|
||||
Justification, Justifications, Storage,
|
||||
Justification, Justifications, StateVersion, Storage,
|
||||
};
|
||||
use sp_state_machine::{
|
||||
Backend as StateBackend, ChildStorageCollection, InMemoryBackend, IndexOperation,
|
||||
@@ -506,6 +506,7 @@ where
|
||||
&mut self,
|
||||
storage: Storage,
|
||||
commit: bool,
|
||||
state_version: StateVersion,
|
||||
) -> sp_blockchain::Result<Block::Hash> {
|
||||
check_genesis_storage(&storage)?;
|
||||
|
||||
@@ -519,6 +520,7 @@ where
|
||||
let (root, transaction) = self.old_state.full_storage_root(
|
||||
storage.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
|
||||
child_delta,
|
||||
state_version,
|
||||
);
|
||||
|
||||
if commit {
|
||||
@@ -566,12 +568,17 @@ where
|
||||
&mut self,
|
||||
storage: Storage,
|
||||
commit: bool,
|
||||
state_version: StateVersion,
|
||||
) -> sp_blockchain::Result<Block::Hash> {
|
||||
self.apply_storage(storage, commit)
|
||||
self.apply_storage(storage, commit, state_version)
|
||||
}
|
||||
|
||||
fn reset_storage(&mut self, storage: Storage) -> sp_blockchain::Result<Block::Hash> {
|
||||
self.apply_storage(storage, true)
|
||||
fn reset_storage(
|
||||
&mut self,
|
||||
storage: Storage,
|
||||
state_version: StateVersion,
|
||||
) -> sp_blockchain::Result<Block::Hash> {
|
||||
self.apply_storage(storage, true, state_version)
|
||||
}
|
||||
|
||||
fn insert_aux<I>(&mut self, ops: I) -> sp_blockchain::Result<()>
|
||||
|
||||
@@ -232,6 +232,7 @@ where
|
||||
header.extrinsics_root().clone(),
|
||||
HashFor::<Block>::ordered_trie_root(
|
||||
self.extrinsics.iter().map(Encode::encode).collect(),
|
||||
sp_runtime::StateVersion::V0,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ use sp_core::{
|
||||
};
|
||||
use sp_runtime::{
|
||||
traits::{Block as BlockT, HashFor},
|
||||
Storage,
|
||||
StateVersion, Storage,
|
||||
};
|
||||
use sp_state_machine::{
|
||||
backend::Backend as StateBackend, ChildStorageCollection, DBValue, ProofRecorder,
|
||||
@@ -73,6 +73,7 @@ impl<Block: BlockT> sp_state_machine::Storage<HashFor<Block>> for StorageDb<Bloc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// State that manages the backend database reference. Allows runtime to control the database.
|
||||
pub struct BenchmarkingState<B: BlockT> {
|
||||
root: Cell<B::Hash>,
|
||||
@@ -105,9 +106,10 @@ impl<B: BlockT> BenchmarkingState<B> {
|
||||
record_proof: bool,
|
||||
enable_tracking: bool,
|
||||
) -> Result<Self, String> {
|
||||
let state_version = sp_runtime::StateVersion::default();
|
||||
let mut root = B::Hash::default();
|
||||
let mut mdb = MemoryDB::<HashFor<B>>::default();
|
||||
sp_state_machine::TrieDBMut::<HashFor<B>>::new(&mut mdb, &mut root);
|
||||
sp_state_machine::TrieDBMutV1::<HashFor<B>>::new(&mut mdb, &mut root);
|
||||
|
||||
let mut state = BenchmarkingState {
|
||||
state: RefCell::new(None),
|
||||
@@ -138,6 +140,7 @@ impl<B: BlockT> BenchmarkingState<B> {
|
||||
state.state.borrow_mut().as_mut().unwrap().full_storage_root(
|
||||
genesis.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
|
||||
child_delta,
|
||||
state_version,
|
||||
);
|
||||
state.genesis = transaction.clone().drain();
|
||||
state.genesis_root = root.clone();
|
||||
@@ -415,6 +418,7 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (B::Hash, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
@@ -422,13 +426,14 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map_or(Default::default(), |s| s.storage_root(delta))
|
||||
.map_or(Default::default(), |s| s.storage_root(delta, state_version))
|
||||
}
|
||||
|
||||
fn child_storage_root<'a>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (B::Hash, bool, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
@@ -436,7 +441,7 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map_or(Default::default(), |s| s.child_storage_root(child_info, delta))
|
||||
.map_or(Default::default(), |s| s.child_storage_root(child_info, delta, state_version))
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
|
||||
@@ -82,7 +82,7 @@ use sp_runtime::{
|
||||
Block as BlockT, Hash, HashFor, Header as HeaderT, NumberFor, One, SaturatedConversion,
|
||||
Zero,
|
||||
},
|
||||
Justification, Justifications, Storage,
|
||||
Justification, Justifications, StateVersion, Storage,
|
||||
};
|
||||
use sp_state_machine::{
|
||||
backend::Backend as StateBackend, ChildStorageCollection, DBValue, IndexOperation,
|
||||
@@ -235,22 +235,24 @@ impl<B: BlockT> StateBackend<HashFor<B>> for RefTrackingState<B> {
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (B::Hash, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
{
|
||||
self.state.storage_root(delta)
|
||||
self.state.storage_root(delta, state_version)
|
||||
}
|
||||
|
||||
fn child_storage_root<'a>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (B::Hash, bool, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
{
|
||||
self.state.child_storage_root(child_info, delta)
|
||||
self.state.child_storage_root(child_info, delta, state_version)
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
@@ -760,7 +762,11 @@ impl<Block: BlockT> BlockImportOperation<Block> {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_new_state(&mut self, storage: Storage) -> ClientResult<Block::Hash> {
|
||||
fn apply_new_state(
|
||||
&mut self,
|
||||
storage: Storage,
|
||||
state_version: StateVersion,
|
||||
) -> ClientResult<Block::Hash> {
|
||||
if storage.top.keys().any(|k| well_known_keys::is_child_storage_key(&k)) {
|
||||
return Err(sp_blockchain::Error::InvalidState.into())
|
||||
}
|
||||
@@ -775,6 +781,7 @@ impl<Block: BlockT> BlockImportOperation<Block> {
|
||||
let (root, transaction) = self.old_state.full_storage_root(
|
||||
storage.top.iter().map(|(k, v)| (&k[..], Some(&v[..]))),
|
||||
child_delta,
|
||||
state_version,
|
||||
);
|
||||
|
||||
self.db_updates = transaction;
|
||||
@@ -814,14 +821,23 @@ impl<Block: BlockT> sc_client_api::backend::BlockImportOperation<Block>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_storage(&mut self, storage: Storage) -> ClientResult<Block::Hash> {
|
||||
let root = self.apply_new_state(storage)?;
|
||||
fn reset_storage(
|
||||
&mut self,
|
||||
storage: Storage,
|
||||
state_version: StateVersion,
|
||||
) -> ClientResult<Block::Hash> {
|
||||
let root = self.apply_new_state(storage, state_version)?;
|
||||
self.commit_state = true;
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
fn set_genesis_state(&mut self, storage: Storage, commit: bool) -> ClientResult<Block::Hash> {
|
||||
let root = self.apply_new_state(storage)?;
|
||||
fn set_genesis_state(
|
||||
&mut self,
|
||||
storage: Storage,
|
||||
commit: bool,
|
||||
state_version: StateVersion,
|
||||
) -> ClientResult<Block::Hash> {
|
||||
let root = self.apply_new_state(storage, state_version)?;
|
||||
self.commit_state = commit;
|
||||
Ok(root)
|
||||
}
|
||||
@@ -924,7 +940,8 @@ impl<Block: BlockT> EmptyStorage<Block> {
|
||||
pub fn new() -> Self {
|
||||
let mut root = Block::Hash::default();
|
||||
let mut mdb = MemoryDB::<HashFor<Block>>::default();
|
||||
sp_state_machine::TrieDBMut::<HashFor<Block>>::new(&mut mdb, &mut root);
|
||||
// both triedbmut are the same on empty storage.
|
||||
sp_state_machine::TrieDBMutV1::<HashFor<Block>>::new(&mut mdb, &mut root);
|
||||
EmptyStorage(root)
|
||||
}
|
||||
}
|
||||
@@ -2262,7 +2279,7 @@ pub(crate) mod tests {
|
||||
use sp_runtime::{
|
||||
testing::{Block as RawBlock, ExtrinsicWrapper, Header},
|
||||
traits::{BlakeTwo256, Hash},
|
||||
ConsensusEngineId,
|
||||
ConsensusEngineId, StateVersion,
|
||||
};
|
||||
|
||||
const CONS0_ENGINE_ID: ConsensusEngineId = *b"CON0";
|
||||
@@ -2295,7 +2312,7 @@ pub(crate) mod tests {
|
||||
let header = Header {
|
||||
number,
|
||||
parent_hash,
|
||||
state_root: BlakeTwo256::trie_root(Vec::new()),
|
||||
state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1),
|
||||
digest,
|
||||
extrinsics_root,
|
||||
};
|
||||
@@ -2375,6 +2392,10 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn set_state_data() {
|
||||
set_state_data_inner(StateVersion::V0);
|
||||
set_state_data_inner(StateVersion::V1);
|
||||
}
|
||||
fn set_state_data_inner(state_version: StateVersion) {
|
||||
let db = Backend::<Block>::new_test(2, 0);
|
||||
let hash = {
|
||||
let mut op = db.begin_operation().unwrap();
|
||||
@@ -2390,15 +2411,18 @@ pub(crate) mod tests {
|
||||
|
||||
header.state_root = op
|
||||
.old_state
|
||||
.storage_root(storage.iter().map(|(x, y)| (&x[..], Some(&y[..]))))
|
||||
.storage_root(storage.iter().map(|(x, y)| (&x[..], Some(&y[..]))), state_version)
|
||||
.0
|
||||
.into();
|
||||
let hash = header.hash();
|
||||
|
||||
op.reset_storage(Storage {
|
||||
top: storage.into_iter().collect(),
|
||||
children_default: Default::default(),
|
||||
})
|
||||
op.reset_storage(
|
||||
Storage {
|
||||
top: storage.into_iter().collect(),
|
||||
children_default: Default::default(),
|
||||
},
|
||||
state_version,
|
||||
)
|
||||
.unwrap();
|
||||
op.set_block_data(header.clone(), Some(vec![]), None, None, NewBlockState::Best)
|
||||
.unwrap();
|
||||
@@ -2427,9 +2451,10 @@ pub(crate) mod tests {
|
||||
|
||||
let storage = vec![(vec![1, 3, 5], None), (vec![5, 5, 5], Some(vec![4, 5, 6]))];
|
||||
|
||||
let (root, overlay) = op
|
||||
.old_state
|
||||
.storage_root(storage.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..]))));
|
||||
let (root, overlay) = op.old_state.storage_root(
|
||||
storage.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..]))),
|
||||
state_version,
|
||||
);
|
||||
op.update_db_storage(overlay).unwrap();
|
||||
header.state_root = root.into();
|
||||
|
||||
@@ -2450,6 +2475,7 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn delete_only_when_negative_rc() {
|
||||
sp_tracing::try_init_simple();
|
||||
let state_version = StateVersion::default();
|
||||
let key;
|
||||
let backend = Backend::<Block>::new_test(1, 0);
|
||||
|
||||
@@ -2466,13 +2492,14 @@ pub(crate) mod tests {
|
||||
extrinsics_root: Default::default(),
|
||||
};
|
||||
|
||||
header.state_root = op.old_state.storage_root(std::iter::empty()).0.into();
|
||||
header.state_root =
|
||||
op.old_state.storage_root(std::iter::empty(), state_version).0.into();
|
||||
let hash = header.hash();
|
||||
|
||||
op.reset_storage(Storage {
|
||||
top: Default::default(),
|
||||
children_default: Default::default(),
|
||||
})
|
||||
op.reset_storage(
|
||||
Storage { top: Default::default(), children_default: Default::default() },
|
||||
state_version,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
key = op.db_updates.insert(EMPTY_PREFIX, b"hello");
|
||||
@@ -2506,7 +2533,7 @@ pub(crate) mod tests {
|
||||
|
||||
header.state_root = op
|
||||
.old_state
|
||||
.storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))))
|
||||
.storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))), state_version)
|
||||
.0
|
||||
.into();
|
||||
let hash = header.hash();
|
||||
@@ -2543,7 +2570,7 @@ pub(crate) mod tests {
|
||||
|
||||
header.state_root = op
|
||||
.old_state
|
||||
.storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))))
|
||||
.storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))), state_version)
|
||||
.0
|
||||
.into();
|
||||
let hash = header.hash();
|
||||
@@ -2577,7 +2604,7 @@ pub(crate) mod tests {
|
||||
|
||||
header.state_root = op
|
||||
.old_state
|
||||
.storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))))
|
||||
.storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))), state_version)
|
||||
.0
|
||||
.into();
|
||||
|
||||
@@ -2912,6 +2939,7 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn storage_hash_is_cached_correctly() {
|
||||
let state_version = StateVersion::default();
|
||||
let backend = Backend::<Block>::new_test(10, 10);
|
||||
|
||||
let hash0 = {
|
||||
@@ -2931,15 +2959,18 @@ pub(crate) mod tests {
|
||||
|
||||
header.state_root = op
|
||||
.old_state
|
||||
.storage_root(storage.iter().map(|(x, y)| (&x[..], Some(&y[..]))))
|
||||
.storage_root(storage.iter().map(|(x, y)| (&x[..], Some(&y[..]))), state_version)
|
||||
.0
|
||||
.into();
|
||||
let hash = header.hash();
|
||||
|
||||
op.reset_storage(Storage {
|
||||
top: storage.into_iter().collect(),
|
||||
children_default: Default::default(),
|
||||
})
|
||||
op.reset_storage(
|
||||
Storage {
|
||||
top: storage.into_iter().collect(),
|
||||
children_default: Default::default(),
|
||||
},
|
||||
state_version,
|
||||
)
|
||||
.unwrap();
|
||||
op.set_block_data(header.clone(), Some(vec![]), None, None, NewBlockState::Best)
|
||||
.unwrap();
|
||||
@@ -2968,9 +2999,10 @@ pub(crate) mod tests {
|
||||
|
||||
let storage = vec![(b"test".to_vec(), Some(b"test2".to_vec()))];
|
||||
|
||||
let (root, overlay) = op
|
||||
.old_state
|
||||
.storage_root(storage.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..]))));
|
||||
let (root, overlay) = op.old_state.storage_root(
|
||||
storage.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..]))),
|
||||
state_version,
|
||||
);
|
||||
op.update_db_storage(overlay).unwrap();
|
||||
header.state_root = root.into();
|
||||
let hash = header.hash();
|
||||
@@ -3212,7 +3244,7 @@ pub(crate) mod tests {
|
||||
let header = Header {
|
||||
number: 1,
|
||||
parent_hash: block0,
|
||||
state_root: BlakeTwo256::trie_root(Vec::new()),
|
||||
state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1),
|
||||
digest: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
};
|
||||
@@ -3224,7 +3256,7 @@ pub(crate) mod tests {
|
||||
let header = Header {
|
||||
number: 2,
|
||||
parent_hash: block1,
|
||||
state_root: BlakeTwo256::trie_root(Vec::new()),
|
||||
state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1),
|
||||
digest: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
};
|
||||
@@ -3247,7 +3279,7 @@ pub(crate) mod tests {
|
||||
let header = Header {
|
||||
number: 1,
|
||||
parent_hash: block0,
|
||||
state_root: BlakeTwo256::trie_root(Vec::new()),
|
||||
state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1),
|
||||
digest: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
};
|
||||
|
||||
@@ -26,7 +26,10 @@ use linked_hash_map::{Entry, LinkedHashMap};
|
||||
use log::trace;
|
||||
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
||||
use sp_core::{hexdisplay::HexDisplay, storage::ChildInfo};
|
||||
use sp_runtime::traits::{Block as BlockT, HashFor, Header, NumberFor};
|
||||
use sp_runtime::{
|
||||
traits::{Block as BlockT, HashFor, Header, NumberFor},
|
||||
StateVersion,
|
||||
};
|
||||
use sp_state_machine::{
|
||||
backend::Backend as StateBackend, ChildStorageCollection, StorageCollection, StorageKey,
|
||||
StorageValue, TrieBackend,
|
||||
@@ -673,22 +676,24 @@ impl<S: StateBackend<HashFor<B>>, B: BlockT> StateBackend<HashFor<B>> for Cachin
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (B::Hash, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
{
|
||||
self.state.storage_root(delta)
|
||||
self.state.storage_root(delta, state_version)
|
||||
}
|
||||
|
||||
fn child_storage_root<'a>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (B::Hash, bool, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
{
|
||||
self.state.child_storage_root(child_info, delta)
|
||||
self.state.child_storage_root(child_info, delta, state_version)
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
@@ -871,22 +876,24 @@ impl<S: StateBackend<HashFor<B>>, B: BlockT> StateBackend<HashFor<B>>
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (B::Hash, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
{
|
||||
self.caching_state().storage_root(delta)
|
||||
self.caching_state().storage_root(delta, state_version)
|
||||
}
|
||||
|
||||
fn child_storage_root<'a>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (B::Hash, bool, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
{
|
||||
self.caching_state().child_storage_root(child_info, delta)
|
||||
self.caching_state().child_storage_root(child_info, delta, state_version)
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
@@ -1182,7 +1189,10 @@ mod tests {
|
||||
|
||||
let shared = new_shared_cache::<Block>(256 * 1024, (0, 1));
|
||||
let mut backend = InMemoryBackend::<BlakeTwo256>::default();
|
||||
backend.insert(std::iter::once((None, vec![(key.clone(), Some(vec![1]))])));
|
||||
backend.insert(
|
||||
std::iter::once((None, vec![(key.clone(), Some(vec![1]))])),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let mut s = CachingState::new(backend.clone(), shared.clone(), Some(root_parent));
|
||||
s.cache.sync_cache(
|
||||
|
||||
@@ -180,6 +180,7 @@ sp_core::wasm_export_functions! {
|
||||
b"one"[..].into(),
|
||||
b"two"[..].into(),
|
||||
],
|
||||
sp_core::storage::StateVersion::V1,
|
||||
).as_ref().to_vec()
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ use sp_core::{
|
||||
};
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use sp_state_machine::TestExternalities as CoreTestExternalities;
|
||||
use sp_trie::{trie_types::Layout, TrieConfiguration};
|
||||
use sp_trie::{LayoutV1 as Layout, TrieConfiguration};
|
||||
use std::sync::Arc;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
|
||||
@@ -215,21 +215,22 @@ fn panicking_should_work(wasm_method: WasmExecutionMethod) {
|
||||
test_wasm_execution!(storage_should_work);
|
||||
fn storage_should_work(wasm_method: WasmExecutionMethod) {
|
||||
let mut ext = TestExternalities::default();
|
||||
// Test value must be bigger than 32 bytes
|
||||
// to test the trie versioning.
|
||||
let value = vec![7u8; 60];
|
||||
|
||||
{
|
||||
let mut ext = ext.ext();
|
||||
ext.set_storage(b"foo".to_vec(), b"bar".to_vec());
|
||||
|
||||
let output =
|
||||
call_in_wasm("test_data_in", &b"Hello world".to_vec().encode(), wasm_method, &mut ext)
|
||||
.unwrap();
|
||||
let output = call_in_wasm("test_data_in", &value.encode(), wasm_method, &mut ext).unwrap();
|
||||
|
||||
assert_eq!(output, b"all ok!".to_vec().encode());
|
||||
}
|
||||
|
||||
let expected = TestExternalities::new(sp_core::storage::Storage {
|
||||
top: map![
|
||||
b"input".to_vec() => b"Hello world".to_vec(),
|
||||
b"input".to_vec() => value,
|
||||
b"foo".to_vec() => b"bar".to_vec(),
|
||||
b"baz".to_vec() => b"bar".to_vec()
|
||||
],
|
||||
|
||||
@@ -334,22 +334,11 @@ where
|
||||
}
|
||||
|
||||
fn decode_version(mut version: &[u8]) -> Result<RuntimeVersion, WasmError> {
|
||||
let v: RuntimeVersion = sp_api::OldRuntimeVersion::decode(&mut &version[..])
|
||||
.map_err(|_| {
|
||||
WasmError::Instantiation(
|
||||
"failed to decode \"Core_version\" result using old runtime version".into(),
|
||||
)
|
||||
})?
|
||||
.into();
|
||||
|
||||
let core_api_id = sp_core_hashing_proc_macro::blake2b_64!(b"Core");
|
||||
if v.has_api_with(&core_api_id, |v| v >= 3) {
|
||||
sp_api::RuntimeVersion::decode(&mut version).map_err(|_| {
|
||||
WasmError::Instantiation("failed to decode \"Core_version\" result".into())
|
||||
})
|
||||
} else {
|
||||
Ok(v)
|
||||
}
|
||||
Decode::decode(&mut version).map_err(|_| {
|
||||
WasmError::Instantiation(
|
||||
"failed to decode \"Core_version\" result using old runtime version".into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn decode_runtime_apis(apis: &[u8]) -> Result<Vec<([u8; 8], u32)>, WasmError> {
|
||||
@@ -373,17 +362,25 @@ fn decode_runtime_apis(apis: &[u8]) -> Result<Vec<([u8; 8], u32)>, WasmError> {
|
||||
/// sections, `Err` will be returned.
|
||||
pub fn read_embedded_version(blob: &RuntimeBlob) -> Result<Option<RuntimeVersion>, WasmError> {
|
||||
if let Some(mut version_section) = blob.custom_section_contents("runtime_version") {
|
||||
// We do not use `decode_version` here because the runtime_version section is not supposed
|
||||
// to ever contain a legacy version. Apart from that `decode_version` relies on presence
|
||||
// of a special API in the `apis` field to treat the input as a non-legacy version. However
|
||||
// the structure found in the `runtime_version` always contain an empty `apis` field.
|
||||
// Therefore the version read will be mistakenly treated as an legacy one.
|
||||
let mut decoded_version = sp_api::RuntimeVersion::decode(&mut version_section)
|
||||
.map_err(|_| WasmError::Instantiation("failed to decode version section".into()))?;
|
||||
let apis = blob
|
||||
.custom_section_contents("runtime_apis")
|
||||
.map(decode_runtime_apis)
|
||||
.transpose()?
|
||||
.map(Into::into);
|
||||
|
||||
// Don't stop on this and check if there is a special section that encodes all runtime APIs.
|
||||
if let Some(apis_section) = blob.custom_section_contents("runtime_apis") {
|
||||
decoded_version.apis = decode_runtime_apis(apis_section)?.into();
|
||||
let core_version = apis.as_ref().and_then(|apis| sp_version::core_version_from_apis(apis));
|
||||
// We do not use `RuntimeVersion::decode` here because that `decode_version` relies on
|
||||
// presence of a special API in the `apis` field to treat the input as a non-legacy version.
|
||||
// However the structure found in the `runtime_version` always contain an empty `apis`
|
||||
// field. Therefore the version read will be mistakenly treated as an legacy one.
|
||||
let mut decoded_version = sp_version::RuntimeVersion::decode_with_version_hint(
|
||||
&mut version_section,
|
||||
core_version,
|
||||
)
|
||||
.map_err(|_| WasmError::Instantiation("failed to decode version section".into()))?;
|
||||
|
||||
if let Some(apis) = apis {
|
||||
decoded_version.apis = apis;
|
||||
}
|
||||
|
||||
Ok(Some(decoded_version))
|
||||
@@ -455,9 +452,20 @@ mod tests {
|
||||
use super::*;
|
||||
use codec::Encode;
|
||||
use sp_api::{Core, RuntimeApiInfo};
|
||||
use sp_runtime::RuntimeString;
|
||||
use sp_wasm_interface::HostFunctions;
|
||||
use substrate_test_runtime::Block;
|
||||
|
||||
#[derive(Encode)]
|
||||
pub struct OldRuntimeVersion {
|
||||
pub spec_name: RuntimeString,
|
||||
pub impl_name: RuntimeString,
|
||||
pub authoring_version: u32,
|
||||
pub spec_version: u32,
|
||||
pub impl_version: u32,
|
||||
pub apis: sp_version::ApisVec,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_functions_are_equal() {
|
||||
let host_functions = sp_io::SubstrateHostFunctions::host_functions();
|
||||
@@ -468,7 +476,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn old_runtime_version_decodes() {
|
||||
let old_runtime_version = sp_api::OldRuntimeVersion {
|
||||
let old_runtime_version = OldRuntimeVersion {
|
||||
spec_name: "test".into(),
|
||||
impl_name: "test".into(),
|
||||
authoring_version: 1,
|
||||
@@ -479,11 +487,12 @@ mod tests {
|
||||
|
||||
let version = decode_version(&old_runtime_version.encode()).unwrap();
|
||||
assert_eq!(1, version.transaction_version);
|
||||
assert_eq!(0, version.state_version);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn old_runtime_version_decodes_fails_with_version_3() {
|
||||
let old_runtime_version = sp_api::OldRuntimeVersion {
|
||||
let old_runtime_version = OldRuntimeVersion {
|
||||
spec_name: "test".into(),
|
||||
impl_name: "test".into(),
|
||||
authoring_version: 1,
|
||||
@@ -505,10 +514,27 @@ mod tests {
|
||||
impl_version: 1,
|
||||
apis: sp_api::create_apis_vec!([(<dyn Core::<Block>>::ID, 3)]),
|
||||
transaction_version: 3,
|
||||
state_version: 4,
|
||||
};
|
||||
|
||||
let version = decode_version(&old_runtime_version.encode()).unwrap();
|
||||
assert_eq!(3, version.transaction_version);
|
||||
assert_eq!(0, version.state_version);
|
||||
|
||||
let old_runtime_version = sp_api::RuntimeVersion {
|
||||
spec_name: "test".into(),
|
||||
impl_name: "test".into(),
|
||||
authoring_version: 1,
|
||||
spec_version: 1,
|
||||
impl_version: 1,
|
||||
apis: sp_api::create_apis_vec!([(<dyn Core::<Block>>::ID, 4)]),
|
||||
transaction_version: 3,
|
||||
state_version: 4,
|
||||
};
|
||||
|
||||
let version = decode_version(&old_runtime_version.encode()).unwrap();
|
||||
assert_eq!(3, version.transaction_version);
|
||||
assert_eq!(4, version.state_version);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -518,15 +544,15 @@ mod tests {
|
||||
sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT,
|
||||
)
|
||||
.expect("Decompressing works");
|
||||
|
||||
let runtime_version = RuntimeVersion {
|
||||
spec_name: "test_replace".into(),
|
||||
impl_name: "test_replace".into(),
|
||||
authoring_version: 100,
|
||||
spec_version: 100,
|
||||
impl_version: 100,
|
||||
apis: sp_api::create_apis_vec!([(<dyn Core::<Block>>::ID, 3)]),
|
||||
apis: sp_api::create_apis_vec!([(<dyn Core::<Block>>::ID, 4)]),
|
||||
transaction_version: 100,
|
||||
state_version: 1,
|
||||
};
|
||||
|
||||
let embedded = sp_version::embed::embed_runtime_version(&wasm, runtime_version.clone())
|
||||
|
||||
@@ -2539,8 +2539,10 @@ fn validate_blocks<Block: BlockT>(
|
||||
}
|
||||
if let (Some(header), Some(body)) = (&b.header, &b.body) {
|
||||
let expected = *header.extrinsics_root();
|
||||
let got =
|
||||
HashFor::<Block>::ordered_trie_root(body.iter().map(Encode::encode).collect());
|
||||
let got = HashFor::<Block>::ordered_trie_root(
|
||||
body.iter().map(Encode::encode).collect(),
|
||||
sp_runtime::StateVersion::V0,
|
||||
);
|
||||
if expected != got {
|
||||
debug!(
|
||||
target:"sync",
|
||||
|
||||
@@ -526,11 +526,11 @@ fn should_return_runtime_version() {
|
||||
);
|
||||
|
||||
let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\
|
||||
\"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",3],\
|
||||
\"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\
|
||||
[\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",5],\
|
||||
[\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",2],\
|
||||
[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\
|
||||
\"transactionVersion\":1}";
|
||||
\"transactionVersion\":1,\"stateVersion\":1}";
|
||||
|
||||
let runtime_version = executor::block_on(api.runtime_version(None.into())).unwrap();
|
||||
let serialized = serde_json::to_string(&runtime_version).unwrap();
|
||||
|
||||
@@ -90,7 +90,7 @@ where
|
||||
Block: BlockT,
|
||||
B: backend::Backend<Block>,
|
||||
{
|
||||
let spec = self.runtime_version(id)?;
|
||||
let spec = CallExecutor::runtime_version(self, id)?;
|
||||
let code = if let Some(d) = self
|
||||
.wasm_override
|
||||
.as_ref()
|
||||
@@ -321,6 +321,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block> RuntimeVersionOf for LocalCallExecutor<Block, B, E>
|
||||
where
|
||||
E: RuntimeVersionOf,
|
||||
Block: BlockT,
|
||||
{
|
||||
fn runtime_version(
|
||||
&self,
|
||||
ext: &mut dyn sp_externalities::Externalities,
|
||||
runtime_code: &sp_core::traits::RuntimeCode,
|
||||
) -> Result<sp_version::RuntimeVersion, sc_executor::error::Error> {
|
||||
RuntimeVersionOf::runtime_version(&self.executor, ext, runtime_code)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, B, E> sp_version::GetRuntimeVersionAt<Block> for LocalCallExecutor<Block, B, E>
|
||||
where
|
||||
B: backend::Backend<Block>,
|
||||
|
||||
@@ -45,7 +45,7 @@ use sc_client_api::{
|
||||
use sc_consensus::{
|
||||
BlockCheckParams, BlockImportParams, ForkChoiceStrategy, ImportResult, StateAction,
|
||||
};
|
||||
use sc_executor::RuntimeVersion;
|
||||
use sc_executor::{RuntimeVersion, RuntimeVersionOf};
|
||||
use sc_telemetry::{telemetry, TelemetryHandle, SUBSTRATE_INFO};
|
||||
use sp_api::{
|
||||
ApiExt, ApiRef, CallApiAt, CallApiAtParams, ConstructRuntimeApi, Core as CoreApi,
|
||||
@@ -60,8 +60,8 @@ use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError};
|
||||
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
|
||||
use sp_core::{
|
||||
storage::{
|
||||
well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, StorageChild, StorageData,
|
||||
StorageKey,
|
||||
well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, Storage, StorageChild,
|
||||
StorageData, StorageKey,
|
||||
},
|
||||
NativeOrEncoded,
|
||||
};
|
||||
@@ -72,7 +72,7 @@ use sp_runtime::{
|
||||
traits::{
|
||||
Block as BlockT, HashFor, Header as HeaderT, NumberFor, One, SaturatedConversion, Zero,
|
||||
},
|
||||
BuildStorage, Digest, Justification, Justifications,
|
||||
BuildStorage, Digest, Justification, Justifications, StateVersion,
|
||||
};
|
||||
use sp_state_machine::{
|
||||
prove_child_read, prove_range_read_with_child_with_size, prove_read,
|
||||
@@ -81,7 +81,7 @@ use sp_state_machine::{
|
||||
};
|
||||
use sp_trie::{CompactProof, StorageProof};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
collections::{hash_map::DefaultHasher, HashMap, HashSet},
|
||||
marker::PhantomData,
|
||||
panic::UnwindSafe,
|
||||
path::PathBuf,
|
||||
@@ -93,7 +93,6 @@ use std::{
|
||||
use {
|
||||
super::call_executor::LocalCallExecutor,
|
||||
sc_client_api::in_mem,
|
||||
sc_executor::RuntimeVersionOf,
|
||||
sp_core::traits::{CodeExecutor, SpawnNamed},
|
||||
};
|
||||
|
||||
@@ -334,8 +333,11 @@ where
|
||||
if info.finalized_state.is_none() {
|
||||
let genesis_storage =
|
||||
build_genesis_storage.build_storage().map_err(sp_blockchain::Error::Storage)?;
|
||||
let genesis_state_version =
|
||||
Self::resolve_state_version_from_wasm(&genesis_storage, &executor)?;
|
||||
let mut op = backend.begin_operation()?;
|
||||
let state_root = op.set_genesis_state(genesis_storage, !config.no_genesis)?;
|
||||
let state_root =
|
||||
op.set_genesis_state(genesis_storage, !config.no_genesis, genesis_state_version)?;
|
||||
let genesis_block = genesis::construct_genesis_block::<Block>(state_root.into());
|
||||
info!(
|
||||
"🔨 Initializing Genesis block/state (state: {}, header-hash: {})",
|
||||
@@ -403,7 +405,7 @@ where
|
||||
|
||||
/// Get the RuntimeVersion at a given block.
|
||||
pub fn runtime_version_at(&self, id: &BlockId<Block>) -> sp_blockchain::Result<RuntimeVersion> {
|
||||
self.executor.runtime_version(id)
|
||||
CallExecutor::runtime_version(&self.executor, id)
|
||||
}
|
||||
|
||||
/// Apply a checked and validated block to an operation. If a justification is provided
|
||||
@@ -606,7 +608,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let state_root = operation.op.reset_storage(storage)?;
|
||||
// This is use by fast sync for runtime version to be resolvable from
|
||||
// changes.
|
||||
let state_version =
|
||||
Self::resolve_state_version_from_wasm(&storage, &self.executor)?;
|
||||
let state_root = operation.op.reset_storage(storage, state_version)?;
|
||||
if state_root != *import_headers.post().state_root() {
|
||||
// State root mismatch when importing state. This should not happen in
|
||||
// safe fast sync mode, but may happen in unsafe mode.
|
||||
@@ -1041,6 +1047,35 @@ where
|
||||
trace!("Collected {} uncles", uncles.len());
|
||||
Ok(uncles)
|
||||
}
|
||||
|
||||
fn resolve_state_version_from_wasm(
|
||||
storage: &Storage,
|
||||
executor: &E,
|
||||
) -> sp_blockchain::Result<StateVersion> {
|
||||
if let Some(wasm) = storage.top.get(well_known_keys::CODE) {
|
||||
let mut ext = sp_state_machine::BasicExternalities::new_empty(); // just to read runtime version.
|
||||
|
||||
let code_fetcher = sp_core::traits::WrappedRuntimeCode(wasm.as_slice().into());
|
||||
let runtime_code = sp_core::traits::RuntimeCode {
|
||||
code_fetcher: &code_fetcher,
|
||||
heap_pages: None,
|
||||
hash: {
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut state = DefaultHasher::new();
|
||||
wasm.hash(&mut state);
|
||||
state.finish().to_le_bytes().to_vec()
|
||||
},
|
||||
};
|
||||
let runtime_version =
|
||||
RuntimeVersionOf::runtime_version(executor, &mut ext, &runtime_code)
|
||||
.map_err(|e| sp_blockchain::Error::VersionInvalid(format!("{:?}", e)))?;
|
||||
Ok(runtime_version.state_version())
|
||||
} else {
|
||||
Err(sp_blockchain::Error::VersionInvalid(
|
||||
"Runtime missing from initial storage, could not read state version.".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block, RA> UsageProvider<Block> for Client<B, E, Block, RA>
|
||||
@@ -1095,12 +1130,14 @@ where
|
||||
size_limit: usize,
|
||||
) -> sp_blockchain::Result<(CompactProof, u32)> {
|
||||
let state = self.state_at(id)?;
|
||||
let root = state.storage_root(std::iter::empty()).0;
|
||||
// this is a read proof, using version V0 or V1 is equivalent.
|
||||
let root = state.storage_root(std::iter::empty(), StateVersion::V0).0;
|
||||
|
||||
let (proof, count) = prove_range_read_with_child_with_size::<_, HashFor<Block>>(
|
||||
state, size_limit, start_key,
|
||||
)?;
|
||||
let proof = sp_trie::encode_compact::<sp_trie::Layout<HashFor<Block>>>(proof, root)
|
||||
// This is read proof only, we can use either LayoutV0 or LayoutV1.
|
||||
let proof = sp_trie::encode_compact::<sp_trie::LayoutV0<HashFor<Block>>>(proof, root)
|
||||
.map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?;
|
||||
Ok((proof, count))
|
||||
}
|
||||
@@ -1225,7 +1262,8 @@ where
|
||||
start_key: &[Vec<u8>],
|
||||
) -> sp_blockchain::Result<(KeyValueStates, usize)> {
|
||||
let mut db = sp_state_machine::MemoryDB::<HashFor<Block>>::new(&[]);
|
||||
let _ = sp_trie::decode_compact::<sp_state_machine::Layout<HashFor<Block>>, _, _>(
|
||||
// Compact encoding
|
||||
let _ = sp_trie::decode_compact::<sp_state_machine::LayoutV0<HashFor<Block>>, _, _>(
|
||||
&mut db,
|
||||
proof.iter_compact_encoded_nodes(),
|
||||
Some(&root),
|
||||
@@ -1594,7 +1632,7 @@ where
|
||||
}
|
||||
|
||||
fn runtime_version_at(&self, at: &BlockId<Block>) -> Result<RuntimeVersion, sp_api::ApiError> {
|
||||
self.runtime_version_at(at).map_err(Into::into)
|
||||
CallExecutor::runtime_version(&self.executor, at).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,10 @@ use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, Zero
|
||||
|
||||
/// Create a genesis block, given the initial storage.
|
||||
pub fn construct_genesis_block<Block: BlockT>(state_root: Block::Hash) -> Block {
|
||||
let extrinsics_root =
|
||||
<<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(Vec::new());
|
||||
let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
Vec::new(),
|
||||
sp_runtime::StateVersion::V0,
|
||||
);
|
||||
|
||||
Block::new(
|
||||
<<Block as BlockT>::Header as HeaderT>::new(
|
||||
|
||||
@@ -34,13 +34,13 @@ use sp_core::{testing::TaskExecutor, H256};
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{BlakeTwo256, Block as BlockT, Header as HeaderT},
|
||||
ConsensusEngineId, Justifications,
|
||||
ConsensusEngineId, Justifications, StateVersion,
|
||||
};
|
||||
use sp_state_machine::{
|
||||
backend::Backend as _, ExecutionStrategy, InMemoryBackend, OverlayedChanges, StateMachine,
|
||||
};
|
||||
use sp_storage::{ChildInfo, StorageKey};
|
||||
use sp_trie::{trie_types::Layout, TrieConfiguration};
|
||||
use sp_trie::{LayoutV0, TrieConfiguration};
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
use substrate_test_runtime::TestAPI;
|
||||
use substrate_test_runtime_client::{
|
||||
@@ -90,7 +90,7 @@ fn construct_block(
|
||||
let transactions = txs.into_iter().map(|tx| tx.into_signed_tx()).collect::<Vec<_>>();
|
||||
|
||||
let iter = transactions.iter().map(Encode::encode);
|
||||
let extrinsics_root = Layout::<BlakeTwo256>::ordered_trie_root(iter).into();
|
||||
let extrinsics_root = LayoutV0::<BlakeTwo256>::ordered_trie_root(iter).into();
|
||||
|
||||
let mut header = Header {
|
||||
parent_hash,
|
||||
@@ -177,7 +177,7 @@ fn construct_genesis_should_work_with_native() {
|
||||
.genesis_map();
|
||||
let genesis_hash = insert_genesis_block(&mut storage);
|
||||
|
||||
let backend = InMemoryBackend::from(storage);
|
||||
let backend = InMemoryBackend::from((storage, StateVersion::default()));
|
||||
let (b1data, _b1hash) = block1(genesis_hash, &backend);
|
||||
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&backend);
|
||||
let runtime_code = backend_runtime_code.runtime_code().expect("Code is part of the backend");
|
||||
@@ -210,7 +210,7 @@ fn construct_genesis_should_work_with_wasm() {
|
||||
.genesis_map();
|
||||
let genesis_hash = insert_genesis_block(&mut storage);
|
||||
|
||||
let backend = InMemoryBackend::from(storage);
|
||||
let backend = InMemoryBackend::from((storage, StateVersion::default()));
|
||||
let (b1data, _b1hash) = block1(genesis_hash, &backend);
|
||||
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&backend);
|
||||
let runtime_code = backend_runtime_code.runtime_code().expect("Code is part of the backend");
|
||||
@@ -243,7 +243,7 @@ fn construct_genesis_with_bad_transaction_should_panic() {
|
||||
.genesis_map();
|
||||
let genesis_hash = insert_genesis_block(&mut storage);
|
||||
|
||||
let backend = InMemoryBackend::from(storage);
|
||||
let backend = InMemoryBackend::from((storage, StateVersion::default()));
|
||||
let (b1data, _b1hash) = block1(genesis_hash, &backend);
|
||||
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&backend);
|
||||
let runtime_code = backend_runtime_code.runtime_code().expect("Code is part of the backend");
|
||||
@@ -418,8 +418,8 @@ fn uncles_with_multiple_forks() {
|
||||
// block tree:
|
||||
// G -> A1 -> A2 -> A3 -> A4 -> A5
|
||||
// A1 -> B2 -> B3 -> B4
|
||||
// B2 -> C3
|
||||
// A1 -> D2
|
||||
// B2 -> C3
|
||||
// A1 -> D2
|
||||
let mut client = substrate_test_runtime_client::new();
|
||||
|
||||
// G -> A1
|
||||
|
||||
@@ -42,8 +42,9 @@ pub use sp_io::storage::root as storage_root;
|
||||
#[doc(hidden)]
|
||||
pub use sp_runtime::traits::Zero;
|
||||
#[doc(hidden)]
|
||||
pub use sp_std::{self, boxed::Box, prelude::Vec, str, vec};
|
||||
pub use sp_runtime::StateVersion;
|
||||
#[doc(hidden)]
|
||||
pub use sp_std::{self, boxed::Box, prelude::Vec, str, vec};
|
||||
pub use sp_storage::TrackedStorageKey;
|
||||
pub use utils::*;
|
||||
|
||||
@@ -1079,7 +1080,7 @@ macro_rules! impl_benchmark {
|
||||
|
||||
// Time the storage root recalculation.
|
||||
let start_storage_root = $crate::benchmarking::current_time();
|
||||
$crate::storage_root();
|
||||
$crate::storage_root($crate::StateVersion::V1);
|
||||
let finish_storage_root = $crate::benchmarking::current_time();
|
||||
let elapsed_storage_root = finish_storage_root - start_storage_root;
|
||||
|
||||
|
||||
@@ -671,7 +671,7 @@ mod tests {
|
||||
|
||||
#[pallet::weight(0)]
|
||||
pub fn calculate_storage_root(_origin: OriginFor<T>) -> DispatchResult {
|
||||
let root = sp_io::storage::root();
|
||||
let root = sp_io::storage::root(sp_runtime::StateVersion::V1);
|
||||
sp_io::storage::set("storage_root".as_bytes(), &root);
|
||||
Ok(())
|
||||
}
|
||||
@@ -897,17 +897,32 @@ mod tests {
|
||||
t.into()
|
||||
}
|
||||
|
||||
fn new_test_ext_v0(balance_factor: Balance) -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
|
||||
pallet_balances::GenesisConfig::<Runtime> { balances: vec![(1, 111 * balance_factor)] }
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
(t, sp_runtime::StateVersion::V0).into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_import_works() {
|
||||
new_test_ext(1).execute_with(|| {
|
||||
block_import_works_inner(
|
||||
new_test_ext_v0(1),
|
||||
hex!("1039e1a4bd0cf5deefe65f313577e70169c41c7773d6acf31ca8d671397559f5").into(),
|
||||
);
|
||||
block_import_works_inner(
|
||||
new_test_ext(1),
|
||||
hex!("75e7d8f360d375bbe91bcf8019c01ab6362448b4a89e3b329717eb9d910340e5").into(),
|
||||
);
|
||||
}
|
||||
fn block_import_works_inner(mut ext: sp_io::TestExternalities, state_root: H256) {
|
||||
ext.execute_with(|| {
|
||||
Executive::execute_block(Block {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!(
|
||||
"1039e1a4bd0cf5deefe65f313577e70169c41c7773d6acf31ca8d671397559f5"
|
||||
)
|
||||
.into(),
|
||||
state_root,
|
||||
extrinsics_root: hex!(
|
||||
"03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"
|
||||
)
|
||||
@@ -948,7 +963,7 @@ mod tests {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!(
|
||||
"49cd58a254ccf6abc4a023d9a22dcfc421e385527a250faec69f8ad0d8ed3e48"
|
||||
"75e7d8f360d375bbe91bcf8019c01ab6362448b4a89e3b329717eb9d910340e5"
|
||||
)
|
||||
.into(),
|
||||
extrinsics_root: [0u8; 32].into(),
|
||||
|
||||
@@ -39,7 +39,7 @@ use sp_session::{MembershipProof, ValidatorCount};
|
||||
use sp_staking::SessionIndex;
|
||||
use sp_std::prelude::*;
|
||||
use sp_trie::{
|
||||
trie_types::{TrieDB, TrieDBMut},
|
||||
trie_types::{TrieDB, TrieDBMutV0},
|
||||
MemoryDB, Recorder, Trie, TrieMut, EMPTY_PREFIX,
|
||||
};
|
||||
|
||||
@@ -236,7 +236,7 @@ impl<T: Config> ProvingTrie<T> {
|
||||
let mut root = Default::default();
|
||||
|
||||
{
|
||||
let mut trie = TrieDBMut::new(&mut db, &mut root);
|
||||
let mut trie = TrieDBMutV0::new(&mut db, &mut root);
|
||||
for (i, (validator, full_id)) in validators.into_iter().enumerate() {
|
||||
let i = i as u32;
|
||||
let keys = match <Session<T>>::load_keys(&validator) {
|
||||
|
||||
@@ -46,7 +46,7 @@ pub use sp_core_hashing_proc_macro;
|
||||
#[doc(hidden)]
|
||||
pub use sp_io::{self, storage::root as storage_root};
|
||||
#[doc(hidden)]
|
||||
pub use sp_runtime::RuntimeDebug;
|
||||
pub use sp_runtime::{RuntimeDebug, StateVersion};
|
||||
#[cfg(feature = "std")]
|
||||
#[doc(hidden)]
|
||||
pub use sp_state_machine::BasicExternalities;
|
||||
@@ -751,9 +751,9 @@ macro_rules! assert_noop {
|
||||
$x:expr,
|
||||
$y:expr $(,)?
|
||||
) => {
|
||||
let h = $crate::storage_root();
|
||||
let h = $crate::storage_root($crate::StateVersion::V1);
|
||||
$crate::assert_err!($x, $y);
|
||||
assert_eq!(h, $crate::storage_root());
|
||||
assert_eq!(h, $crate::storage_root($crate::StateVersion::V1));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -766,9 +766,9 @@ macro_rules! assert_storage_noop {
|
||||
(
|
||||
$x:expr
|
||||
) => {
|
||||
let h = $crate::storage_root();
|
||||
let h = $crate::storage_root($crate::StateVersion::V1);
|
||||
$x;
|
||||
assert_eq!(h, $crate::storage_root());
|
||||
assert_eq!(h, $crate::storage_root($crate::StateVersion::V1));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
pub use crate::sp_io::KillStorageResult;
|
||||
use crate::sp_std::prelude::*;
|
||||
use codec::{Codec, Decode, Encode};
|
||||
pub use sp_core::storage::{ChildInfo, ChildType};
|
||||
pub use sp_core::storage::{ChildInfo, ChildType, StateVersion};
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T: Decode + Sized>(child_info: &ChildInfo, key: &[u8]) -> Option<T> {
|
||||
@@ -167,9 +167,10 @@ pub fn put_raw(child_info: &ChildInfo, key: &[u8], value: &[u8]) {
|
||||
}
|
||||
|
||||
/// Calculate current child root value.
|
||||
pub fn root(child_info: &ChildInfo) -> Vec<u8> {
|
||||
pub fn root(child_info: &ChildInfo, version: StateVersion) -> Vec<u8> {
|
||||
match child_info.child_type() {
|
||||
ChildType::ParentKeyId => sp_io::default_child_storage::root(child_info.storage_key()),
|
||||
ChildType::ParentKeyId =>
|
||||
sp_io::default_child_storage::root(child_info.storage_key(), version),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
impl_version: 0,
|
||||
apis: sp_version::create_apis_vec!([]),
|
||||
transaction_version: 0,
|
||||
state_version: 0,
|
||||
};
|
||||
|
||||
pub type Signature = sr25519::Signature;
|
||||
|
||||
@@ -127,13 +127,19 @@ pub use extensions::check_mortality::CheckMortality as CheckEra;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
/// Compute the trie root of a list of extrinsics.
|
||||
///
|
||||
/// The merkle proof is using the same trie as runtime state with
|
||||
/// `state_version` 0.
|
||||
pub fn extrinsics_root<H: Hash, E: codec::Encode>(extrinsics: &[E]) -> H::Output {
|
||||
extrinsics_data_root::<H>(extrinsics.iter().map(codec::Encode::encode).collect())
|
||||
}
|
||||
|
||||
/// Compute the trie root of a list of extrinsics.
|
||||
///
|
||||
/// The merkle proof is using the same trie as runtime state with
|
||||
/// `state_version` 0.
|
||||
pub fn extrinsics_data_root<H: Hash>(xts: Vec<Vec<u8>>) -> H::Output {
|
||||
H::ordered_trie_root(xts)
|
||||
H::ordered_trie_root(xts, sp_core::storage::StateVersion::V0)
|
||||
}
|
||||
|
||||
/// An object to track the currently used extrinsic weight in a block.
|
||||
@@ -1355,7 +1361,8 @@ impl<T: Config> Pallet<T> {
|
||||
<BlockHash<T>>::remove(to_remove);
|
||||
}
|
||||
|
||||
let storage_root = T::Hash::decode(&mut &sp_io::storage::root()[..])
|
||||
let version = T::Version::get().state_version();
|
||||
let storage_root = T::Hash::decode(&mut &sp_io::storage::root(version)[..])
|
||||
.expect("Node is configured to use the same hash; qed");
|
||||
|
||||
<T::Header as traits::Header>::new(
|
||||
|
||||
@@ -53,6 +53,7 @@ parameter_types! {
|
||||
impl_version: 1,
|
||||
apis: sp_version::create_apis_vec!([]),
|
||||
transaction_version: 1,
|
||||
state_version: 1,
|
||||
};
|
||||
pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
|
||||
read: 10,
|
||||
|
||||
@@ -29,54 +29,77 @@ use sp_transaction_storage_proof::TransactionStorageProof;
|
||||
|
||||
use crate::Pallet as TransactionStorage;
|
||||
|
||||
// Proof generated from max size storage:
|
||||
// ```
|
||||
// let mut transactions = Vec::new();
|
||||
// let tx_size = DEFAULT_MAX_TRANSACTION_SIZE;
|
||||
// for _ in 0..DEFAULT_MAX_BLOCK_TRANSACTIONS {
|
||||
// transactions.push(vec![0; tx_size]);
|
||||
// }
|
||||
// let hash = vec![0; 32];
|
||||
// build_proof(hash.as_slice(), transactions).unwrap().encode()
|
||||
// ```
|
||||
// while hardforcing target chunk key in `build_proof` to [22, 21, 1, 0].
|
||||
const PROOF: &[u8] = &hex_literal::hex!(
|
||||
"
|
||||
0104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
000000000000000000000000000000014cd0780ffff80e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe8
|
||||
7d12a3662c4c0080e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c80e316a478e2f1fcb
|
||||
13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c80e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2
|
||||
f3dfe87d12a3662c4c80e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c80e316a478e2f
|
||||
1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c80e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f
|
||||
3ca2f3dfe87d12a3662c4c80e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c80e316a47
|
||||
8e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c80e316a478e2f1fcb13cf22fd0b2dbb54a6f53cf
|
||||
f93f3ca2f3dfe87d12a3662c4c80e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c80e31
|
||||
6a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c80e316a478e2f1fcb13cf22fd0b2dbb54a6f
|
||||
53cff93f3ca2f3dfe87d12a3662c4c80e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4c8
|
||||
0e316a478e2f1fcb13cf22fd0b2dbb54a6f53cff93f3ca2f3dfe87d12a3662c4cbd05807777809a5d7a720ce5f9d9a012
|
||||
fbf25e92c30e732dadba8f312b05e02976313ea64d9f807d43bcbf8a3dc2f6b9e957d129e610c06d411e11743062dc1cf
|
||||
3ac289390ae4c8008592aa2d915f52941036afbe72bac4ebe7ce186c4ddc53f118e0ddd4decd8cc809a5d7a720ce5f9d9
|
||||
a012fbf25e92c30e732dadba8f312b05e02976313ea64d9f807d43bcbf8a3dc2f6b9e957d129e610c06d411e11743062d
|
||||
c1cf3ac289390ae4c00809a5d7a720ce5f9d9a012fbf25e92c30e732dadba8f312b05e02976313ea64d9f807d43bcbf8a
|
||||
3dc2f6b9e957d129e610c06d411e11743062dc1cf3ac289390ae4c8008592aa2d915f52941036afbe72bac4ebe7ce186c
|
||||
4ddc53f118e0ddd4decd8cc809a5d7a720ce5f9d9a012fbf25e92c30e732dadba8f312b05e02976313ea64d9f807d43bc
|
||||
bf8a3dc2f6b9e957d129e610c06d411e11743062dc1cf3ac289390ae4c8008592aa2d915f52941036afbe72bac4ebe7ce
|
||||
186c4ddc53f118e0ddd4decd8cccd0780ffff8081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb0
|
||||
3bdb31008081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b825bfa9b2ba8f5f253
|
||||
515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa139
|
||||
8e0cb03bdb318081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b825bfa9b2ba8f5
|
||||
f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3a
|
||||
a1398e0cb03bdb318081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b825bfa9b2b
|
||||
a8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f32
|
||||
2d3aa1398e0cb03bdb318081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b825bfa
|
||||
9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f0
|
||||
2f322d3aa1398e0cb03bdb318081b825bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb318081b82
|
||||
5bfa9b2ba8f5f253515e7db09eb1ad3d4f02f322d3aa1398e0cb03bdb31cd0780ffff80b4f23ac50c8e67d9b280f2b31a
|
||||
5707d52b892977acaac84d530bd188544c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b892977acaac84d530bd1885
|
||||
44c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b892977acaac84d530bd188544c5f9b80b4f23ac50c8e67d9b280f2
|
||||
b31a5707d52b892977acaac84d530bd188544c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b892977acaac84d530bd
|
||||
188544c5f9b0080b4f23ac50c8e67d9b280f2b31a5707d52b892977acaac84d530bd188544c5f9b80b4f23ac50c8e67d9
|
||||
b280f2b31a5707d52b892977acaac84d530bd188544c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b892977acaac84
|
||||
d530bd188544c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b892977acaac84d530bd188544c5f9b80b4f23ac50c8e
|
||||
67d9b280f2b31a5707d52b892977acaac84d530bd188544c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b892977aca
|
||||
ac84d530bd188544c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b892977acaac84d530bd188544c5f9b80b4f23ac5
|
||||
0c8e67d9b280f2b31a5707d52b892977acaac84d530bd188544c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b89297
|
||||
7acaac84d530bd188544c5f9b80b4f23ac50c8e67d9b280f2b31a5707d52b892977acaac84d530bd188544c5f9b104401
|
||||
0000
|
||||
"
|
||||
0104000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
00000000000000000000000000000000000000000000000000000000000014cd0780ffff8030
|
||||
2eb0a6d2f63b834d15f1e729d1c1004657e3048cf206d697eeb153f61a30ba0080302eb0a6d2
|
||||
f63b834d15f1e729d1c1004657e3048cf206d697eeb153f61a30ba80302eb0a6d2f63b834d15
|
||||
f1e729d1c1004657e3048cf206d697eeb153f61a30ba80302eb0a6d2f63b834d15f1e729d1c1
|
||||
004657e3048cf206d697eeb153f61a30ba80302eb0a6d2f63b834d15f1e729d1c1004657e304
|
||||
8cf206d697eeb153f61a30ba80302eb0a6d2f63b834d15f1e729d1c1004657e3048cf206d697
|
||||
eeb153f61a30ba80302eb0a6d2f63b834d15f1e729d1c1004657e3048cf206d697eeb153f61a
|
||||
30ba80302eb0a6d2f63b834d15f1e729d1c1004657e3048cf206d697eeb153f61a30ba80302e
|
||||
b0a6d2f63b834d15f1e729d1c1004657e3048cf206d697eeb153f61a30ba80302eb0a6d2f63b
|
||||
834d15f1e729d1c1004657e3048cf206d697eeb153f61a30ba80302eb0a6d2f63b834d15f1e7
|
||||
29d1c1004657e3048cf206d697eeb153f61a30ba80302eb0a6d2f63b834d15f1e729d1c10046
|
||||
57e3048cf206d697eeb153f61a30ba80302eb0a6d2f63b834d15f1e729d1c1004657e3048cf2
|
||||
06d697eeb153f61a30ba80302eb0a6d2f63b834d15f1e729d1c1004657e3048cf206d697eeb1
|
||||
53f61a30ba80302eb0a6d2f63b834d15f1e729d1c1004657e3048cf206d697eeb153f61a30ba
|
||||
bd058077778010fd81bc1359802f0b871aeb95e4410a8ec92b93af10ea767a2027cf4734e8de
|
||||
808da338e6b722f7bf2051901bd5bccee5e71d5cf6b1faff338ad7120b0256c28380221ce17f
|
||||
19117affa96e077905fe48a99723a065969c638593b7d9ab57b538438010fd81bc1359802f0b
|
||||
871aeb95e4410a8ec92b93af10ea767a2027cf4734e8de808da338e6b722f7bf2051901bd5bc
|
||||
cee5e71d5cf6b1faff338ad7120b0256c283008010fd81bc1359802f0b871aeb95e4410a8ec9
|
||||
2b93af10ea767a2027cf4734e8de808da338e6b722f7bf2051901bd5bccee5e71d5cf6b1faff
|
||||
338ad7120b0256c28380221ce17f19117affa96e077905fe48a99723a065969c638593b7d9ab
|
||||
57b538438010fd81bc1359802f0b871aeb95e4410a8ec92b93af10ea767a2027cf4734e8de80
|
||||
8da338e6b722f7bf2051901bd5bccee5e71d5cf6b1faff338ad7120b0256c28380221ce17f19
|
||||
117affa96e077905fe48a99723a065969c638593b7d9ab57b53843cd0780ffff804509f59593
|
||||
fd47b1a97189127ba65a5649cfb0346637f9836e155eaf891a939c00804509f59593fd47b1a9
|
||||
7189127ba65a5649cfb0346637f9836e155eaf891a939c804509f59593fd47b1a97189127ba6
|
||||
5a5649cfb0346637f9836e155eaf891a939c804509f59593fd47b1a97189127ba65a5649cfb0
|
||||
346637f9836e155eaf891a939c804509f59593fd47b1a97189127ba65a5649cfb0346637f983
|
||||
6e155eaf891a939c804509f59593fd47b1a97189127ba65a5649cfb0346637f9836e155eaf89
|
||||
1a939c804509f59593fd47b1a97189127ba65a5649cfb0346637f9836e155eaf891a939c8045
|
||||
09f59593fd47b1a97189127ba65a5649cfb0346637f9836e155eaf891a939c804509f59593fd
|
||||
47b1a97189127ba65a5649cfb0346637f9836e155eaf891a939c804509f59593fd47b1a97189
|
||||
127ba65a5649cfb0346637f9836e155eaf891a939c804509f59593fd47b1a97189127ba65a56
|
||||
49cfb0346637f9836e155eaf891a939c804509f59593fd47b1a97189127ba65a5649cfb03466
|
||||
37f9836e155eaf891a939c804509f59593fd47b1a97189127ba65a5649cfb0346637f9836e15
|
||||
5eaf891a939c804509f59593fd47b1a97189127ba65a5649cfb0346637f9836e155eaf891a93
|
||||
9c804509f59593fd47b1a97189127ba65a5649cfb0346637f9836e155eaf891a939ccd0780ff
|
||||
ff8078916e776c64ccea05e958559f015c082d9d06feafa3610fc44a5b2ef543cb818078916e
|
||||
776c64ccea05e958559f015c082d9d06feafa3610fc44a5b2ef543cb818078916e776c64ccea
|
||||
05e958559f015c082d9d06feafa3610fc44a5b2ef543cb818078916e776c64ccea05e958559f
|
||||
015c082d9d06feafa3610fc44a5b2ef543cb818078916e776c64ccea05e958559f015c082d9d
|
||||
06feafa3610fc44a5b2ef543cb81008078916e776c64ccea05e958559f015c082d9d06feafa3
|
||||
610fc44a5b2ef543cb818078916e776c64ccea05e958559f015c082d9d06feafa3610fc44a5b
|
||||
2ef543cb818078916e776c64ccea05e958559f015c082d9d06feafa3610fc44a5b2ef543cb81
|
||||
8078916e776c64ccea05e958559f015c082d9d06feafa3610fc44a5b2ef543cb818078916e77
|
||||
6c64ccea05e958559f015c082d9d06feafa3610fc44a5b2ef543cb818078916e776c64ccea05
|
||||
e958559f015c082d9d06feafa3610fc44a5b2ef543cb818078916e776c64ccea05e958559f01
|
||||
5c082d9d06feafa3610fc44a5b2ef543cb818078916e776c64ccea05e958559f015c082d9d06
|
||||
feafa3610fc44a5b2ef543cb818078916e776c64ccea05e958559f015c082d9d06feafa3610f
|
||||
c44a5b2ef543cb818078916e776c64ccea05e958559f015c082d9d06feafa3610fc44a5b2ef5
|
||||
43cb811044010000
|
||||
"
|
||||
);
|
||||
|
||||
type BalanceOf<T> =
|
||||
@@ -136,7 +159,6 @@ benchmarks! {
|
||||
)?;
|
||||
}
|
||||
run_to_block::<T>(StoragePeriod::<T>::get() + T::BlockNumber::one());
|
||||
let random_hash = [0u8];
|
||||
let mut encoded_proof = PROOF;
|
||||
let proof = TransactionStorageProof::decode(&mut encoded_proof).unwrap();
|
||||
}: check_proof(RawOrigin::None, proof)
|
||||
|
||||
@@ -188,7 +188,7 @@ pub mod pallet {
|
||||
// Chunk data and compute storage root
|
||||
let chunk_count = num_chunks(data.len() as u32);
|
||||
let chunks = data.chunks(CHUNK_SIZE).map(|c| c.to_vec()).collect();
|
||||
let root = sp_io::trie::blake2_256_ordered_root(chunks);
|
||||
let root = sp_io::trie::blake2_256_ordered_root(chunks, sp_runtime::StateVersion::V1);
|
||||
|
||||
let content_hash = sp_io::hashing::blake2_256(&data);
|
||||
let extrinsic_index = <frame_system::Pallet<T>>::extrinsic_index()
|
||||
@@ -300,6 +300,7 @@ pub mod pallet {
|
||||
&proof.proof,
|
||||
&encode_index(chunk_index),
|
||||
&proof.chunk,
|
||||
sp_runtime::StateVersion::V1,
|
||||
),
|
||||
Error::<T>::InvalidProof
|
||||
);
|
||||
|
||||
@@ -293,10 +293,17 @@ fn generate_runtime_api_base_structures() -> Result<TokenStream> {
|
||||
#crate_::StorageChanges<C::StateBackend, Block>,
|
||||
String
|
||||
> where Self: Sized {
|
||||
let at = #crate_::BlockId::Hash(parent_hash.clone());
|
||||
let state_version = self.call
|
||||
.runtime_version_at(&at)
|
||||
.map(|v| v.state_version())
|
||||
.map_err(|e| format!("Failed to get state version: {:?}", e))?;
|
||||
|
||||
self.changes.replace(Default::default()).into_storage_changes(
|
||||
backend,
|
||||
parent_hash,
|
||||
self.storage_transaction_cache.replace(Default::default()),
|
||||
state_version,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ pub use sp_core::NativeOrEncoded;
|
||||
use sp_core::OpaqueMetadata;
|
||||
#[doc(hidden)]
|
||||
pub use sp_core::{offchain, ExecutionContext};
|
||||
#[cfg(feature = "std")]
|
||||
pub use sp_runtime::StateVersion;
|
||||
#[doc(hidden)]
|
||||
pub use sp_runtime::{
|
||||
generic::BlockId,
|
||||
@@ -269,6 +271,7 @@ pub use sp_api_proc_macro::decl_runtime_apis;
|
||||
/// // Here we are exposing the runtime api versions.
|
||||
/// apis: RUNTIME_API_VERSIONS,
|
||||
/// transaction_version: 1,
|
||||
/// state_version: 1,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() {}
|
||||
@@ -654,53 +657,13 @@ pub fn deserialize_runtime_api_info(bytes: [u8; RUNTIME_API_INFO_SIZE]) -> ([u8;
|
||||
(id, version)
|
||||
}
|
||||
|
||||
#[derive(codec::Encode, codec::Decode)]
|
||||
pub struct OldRuntimeVersion {
|
||||
pub spec_name: RuntimeString,
|
||||
pub impl_name: RuntimeString,
|
||||
pub authoring_version: u32,
|
||||
pub spec_version: u32,
|
||||
pub impl_version: u32,
|
||||
pub apis: ApisVec,
|
||||
}
|
||||
|
||||
impl From<OldRuntimeVersion> for RuntimeVersion {
|
||||
fn from(x: OldRuntimeVersion) -> Self {
|
||||
Self {
|
||||
spec_name: x.spec_name,
|
||||
impl_name: x.impl_name,
|
||||
authoring_version: x.authoring_version,
|
||||
spec_version: x.spec_version,
|
||||
impl_version: x.impl_version,
|
||||
apis: x.apis,
|
||||
transaction_version: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RuntimeVersion> for OldRuntimeVersion {
|
||||
fn from(x: RuntimeVersion) -> Self {
|
||||
Self {
|
||||
spec_name: x.spec_name,
|
||||
impl_name: x.impl_name,
|
||||
authoring_version: x.authoring_version,
|
||||
spec_version: x.spec_version,
|
||||
impl_version: x.impl_version,
|
||||
apis: x.apis,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decl_runtime_apis! {
|
||||
/// The `Core` runtime api that every Substrate runtime needs to implement.
|
||||
#[core_trait]
|
||||
#[api_version(3)]
|
||||
#[api_version(4)]
|
||||
pub trait Core {
|
||||
/// Returns the version of the runtime.
|
||||
fn version() -> RuntimeVersion;
|
||||
/// Returns the version of the runtime.
|
||||
#[changed_in(3)]
|
||||
fn version() -> OldRuntimeVersion;
|
||||
/// Execute the given block.
|
||||
fn execute_block(block: Block);
|
||||
/// Initialize a block with the given header.
|
||||
|
||||
@@ -31,7 +31,7 @@ use sp_std::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use sp_storage::{ChildInfo, TrackedStorageKey};
|
||||
use sp_storage::{ChildInfo, StateVersion, TrackedStorageKey};
|
||||
|
||||
pub use extensions::{Extension, ExtensionStore, Extensions};
|
||||
pub use scope_limited::{set_and_run_with_externalities, with_externalities};
|
||||
@@ -157,7 +157,7 @@ pub trait Externalities: ExtensionStore {
|
||||
/// This will also update all child storage keys in the top-level storage map.
|
||||
///
|
||||
/// The returned hash is defined by the `Block` and is SCALE encoded.
|
||||
fn storage_root(&mut self) -> Vec<u8>;
|
||||
fn storage_root(&mut self, state_version: StateVersion) -> Vec<u8>;
|
||||
|
||||
/// Get the trie root of a child storage map.
|
||||
///
|
||||
@@ -165,7 +165,11 @@ pub trait Externalities: ExtensionStore {
|
||||
///
|
||||
/// If the storage root equals the default hash as defined by the trie, the key in the top-level
|
||||
/// storage map will be removed.
|
||||
fn child_storage_root(&mut self, child_info: &ChildInfo) -> Vec<u8>;
|
||||
fn child_storage_root(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
state_version: StateVersion,
|
||||
) -> Vec<u8>;
|
||||
|
||||
/// Append storage item.
|
||||
///
|
||||
|
||||
@@ -51,11 +51,13 @@ use sp_core::{
|
||||
offchain::{
|
||||
HttpError, HttpRequestId, HttpRequestStatus, OpaqueNetworkState, StorageKind, Timestamp,
|
||||
},
|
||||
sr25519, LogLevel, LogLevelFilter, OpaquePeerId, H256,
|
||||
sr25519,
|
||||
storage::StateVersion,
|
||||
LogLevel, LogLevelFilter, OpaquePeerId, H256,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use sp_trie::{trie_types::Layout, TrieConfiguration};
|
||||
use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration};
|
||||
|
||||
use sp_runtime_interface::{
|
||||
pass_by::{PassBy, PassByCodec},
|
||||
@@ -192,7 +194,17 @@ pub trait Storage {
|
||||
///
|
||||
/// Returns a `Vec<u8>` that holds the SCALE encoded hash.
|
||||
fn root(&mut self) -> Vec<u8> {
|
||||
self.storage_root()
|
||||
self.storage_root(StateVersion::V0)
|
||||
}
|
||||
|
||||
/// "Commit" all existing operations and compute the resulting storage root.
|
||||
///
|
||||
/// The hashing algorithm is defined by the `Block`.
|
||||
///
|
||||
/// Returns a `Vec<u8>` that holds the SCALE encoded hash.
|
||||
#[version(2)]
|
||||
fn root(&mut self, version: StateVersion) -> Vec<u8> {
|
||||
self.storage_root(version)
|
||||
}
|
||||
|
||||
/// Always returns `None`. This function exists for compatibility reasons.
|
||||
@@ -373,7 +385,19 @@ pub trait DefaultChildStorage {
|
||||
/// Returns a `Vec<u8>` that holds the SCALE encoded hash.
|
||||
fn root(&mut self, storage_key: &[u8]) -> Vec<u8> {
|
||||
let child_info = ChildInfo::new_default(storage_key);
|
||||
self.child_storage_root(&child_info)
|
||||
self.child_storage_root(&child_info, StateVersion::V0)
|
||||
}
|
||||
|
||||
/// Default child root calculation.
|
||||
///
|
||||
/// "Commit" all existing operations and compute the resulting child storage root.
|
||||
/// The hashing algorithm is defined by the `Block`.
|
||||
///
|
||||
/// Returns a `Vec<u8>` that holds the SCALE encoded hash.
|
||||
#[version(2)]
|
||||
fn root(&mut self, storage_key: &[u8], version: StateVersion) -> Vec<u8> {
|
||||
let child_info = ChildInfo::new_default(storage_key);
|
||||
self.child_storage_root(&child_info, version)
|
||||
}
|
||||
|
||||
/// Child storage key iteration.
|
||||
@@ -390,27 +414,63 @@ pub trait DefaultChildStorage {
|
||||
pub trait Trie {
|
||||
/// A trie root formed from the iterated items.
|
||||
fn blake2_256_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
|
||||
Layout::<sp_core::Blake2Hasher>::trie_root(input)
|
||||
LayoutV0::<sp_core::Blake2Hasher>::trie_root(input)
|
||||
}
|
||||
|
||||
/// A trie root formed from the iterated items.
|
||||
#[version(2)]
|
||||
fn blake2_256_root(input: Vec<(Vec<u8>, Vec<u8>)>, version: StateVersion) -> H256 {
|
||||
match version {
|
||||
StateVersion::V0 => LayoutV0::<sp_core::Blake2Hasher>::trie_root(input),
|
||||
StateVersion::V1 => LayoutV1::<sp_core::Blake2Hasher>::trie_root(input),
|
||||
}
|
||||
}
|
||||
|
||||
/// A trie root formed from the enumerated items.
|
||||
fn blake2_256_ordered_root(input: Vec<Vec<u8>>) -> H256 {
|
||||
Layout::<sp_core::Blake2Hasher>::ordered_trie_root(input)
|
||||
LayoutV0::<sp_core::Blake2Hasher>::ordered_trie_root(input)
|
||||
}
|
||||
|
||||
/// A trie root formed from the enumerated items.
|
||||
#[version(2)]
|
||||
fn blake2_256_ordered_root(input: Vec<Vec<u8>>, version: StateVersion) -> H256 {
|
||||
match version {
|
||||
StateVersion::V0 => LayoutV0::<sp_core::Blake2Hasher>::ordered_trie_root(input),
|
||||
StateVersion::V1 => LayoutV1::<sp_core::Blake2Hasher>::ordered_trie_root(input),
|
||||
}
|
||||
}
|
||||
|
||||
/// A trie root formed from the iterated items.
|
||||
fn keccak_256_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
|
||||
Layout::<sp_core::KeccakHasher>::trie_root(input)
|
||||
LayoutV0::<sp_core::KeccakHasher>::trie_root(input)
|
||||
}
|
||||
|
||||
/// A trie root formed from the iterated items.
|
||||
#[version(2)]
|
||||
fn keccak_256_root(input: Vec<(Vec<u8>, Vec<u8>)>, version: StateVersion) -> H256 {
|
||||
match version {
|
||||
StateVersion::V0 => LayoutV0::<sp_core::KeccakHasher>::trie_root(input),
|
||||
StateVersion::V1 => LayoutV1::<sp_core::KeccakHasher>::trie_root(input),
|
||||
}
|
||||
}
|
||||
|
||||
/// A trie root formed from the enumerated items.
|
||||
fn keccak_256_ordered_root(input: Vec<Vec<u8>>) -> H256 {
|
||||
Layout::<sp_core::KeccakHasher>::ordered_trie_root(input)
|
||||
LayoutV0::<sp_core::KeccakHasher>::ordered_trie_root(input)
|
||||
}
|
||||
|
||||
/// A trie root formed from the enumerated items.
|
||||
#[version(2)]
|
||||
fn keccak_256_ordered_root(input: Vec<Vec<u8>>, version: StateVersion) -> H256 {
|
||||
match version {
|
||||
StateVersion::V0 => LayoutV0::<sp_core::KeccakHasher>::ordered_trie_root(input),
|
||||
StateVersion::V1 => LayoutV1::<sp_core::KeccakHasher>::ordered_trie_root(input),
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify trie proof
|
||||
fn blake2_256_verify_proof(root: H256, proof: &[Vec<u8>], key: &[u8], value: &[u8]) -> bool {
|
||||
sp_trie::verify_trie_proof::<Layout<sp_core::Blake2Hasher>, _, _, _>(
|
||||
sp_trie::verify_trie_proof::<LayoutV0<sp_core::Blake2Hasher>, _, _, _>(
|
||||
&root,
|
||||
proof,
|
||||
&[(key, Some(value))],
|
||||
@@ -418,15 +478,69 @@ pub trait Trie {
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Verify trie proof
|
||||
#[version(2)]
|
||||
fn blake2_256_verify_proof(
|
||||
root: H256,
|
||||
proof: &[Vec<u8>],
|
||||
key: &[u8],
|
||||
value: &[u8],
|
||||
version: StateVersion,
|
||||
) -> bool {
|
||||
match version {
|
||||
StateVersion::V0 => sp_trie::verify_trie_proof::<
|
||||
LayoutV0<sp_core::Blake2Hasher>,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(&root, proof, &[(key, Some(value))])
|
||||
.is_ok(),
|
||||
StateVersion::V1 => sp_trie::verify_trie_proof::<
|
||||
LayoutV1<sp_core::Blake2Hasher>,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(&root, proof, &[(key, Some(value))])
|
||||
.is_ok(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify trie proof
|
||||
fn keccak_256_verify_proof(root: H256, proof: &[Vec<u8>], key: &[u8], value: &[u8]) -> bool {
|
||||
sp_trie::verify_trie_proof::<Layout<sp_core::KeccakHasher>, _, _, _>(
|
||||
sp_trie::verify_trie_proof::<LayoutV0<sp_core::KeccakHasher>, _, _, _>(
|
||||
&root,
|
||||
proof,
|
||||
&[(key, Some(value))],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Verify trie proof
|
||||
#[version(2)]
|
||||
fn keccak_256_verify_proof(
|
||||
root: H256,
|
||||
proof: &[Vec<u8>],
|
||||
key: &[u8],
|
||||
value: &[u8],
|
||||
version: StateVersion,
|
||||
) -> bool {
|
||||
match version {
|
||||
StateVersion::V0 => sp_trie::verify_trie_proof::<
|
||||
LayoutV0<sp_core::KeccakHasher>,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(&root, proof, &[(key, Some(value))])
|
||||
.is_ok(),
|
||||
StateVersion::V1 => sp_trie::verify_trie_proof::<
|
||||
LayoutV1<sp_core::KeccakHasher>,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(&root, proof, &[(key, Some(value))])
|
||||
.is_ok(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface that provides miscellaneous functions for communicating between the runtime and the
|
||||
@@ -1570,6 +1684,16 @@ mod tests {
|
||||
assert_eq!(storage::get(b"hello"), None);
|
||||
assert_eq!(storage::get(b"foo"), Some(b"bar".to_vec()));
|
||||
});
|
||||
|
||||
let value = vec![7u8; 35];
|
||||
let storage =
|
||||
Storage { top: map![b"foo00".to_vec() => value.clone()], children_default: map![] };
|
||||
t = BasicExternalities::new(storage);
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(storage::get(b"hello"), None);
|
||||
assert_eq!(storage::get(b"foo00"), Some(value.clone()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -544,3 +544,7 @@ impl PassBy for sp_wasm_interface::Value {
|
||||
impl PassBy for sp_storage::TrackedStorageKey {
|
||||
type PassBy = Codec<Self>;
|
||||
}
|
||||
|
||||
impl PassBy for sp_storage::StateVersion {
|
||||
type PassBy = Enum<Self>;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ pub use paste;
|
||||
#[doc(hidden)]
|
||||
pub use sp_application_crypto as app_crypto;
|
||||
|
||||
pub use sp_core::storage::StateVersion;
|
||||
#[cfg(feature = "std")]
|
||||
pub use sp_core::storage::{Storage, StorageChild};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ pub use sp_arithmetic::traits::{
|
||||
CheckedShr, CheckedSub, IntegerSquareRoot, One, SaturatedConversion, Saturating,
|
||||
UniqueSaturatedFrom, UniqueSaturatedInto, Zero,
|
||||
};
|
||||
use sp_core::{self, Hasher, RuntimeDebug, TypeId};
|
||||
use sp_core::{self, storage::StateVersion, Hasher, RuntimeDebug, TypeId};
|
||||
use sp_std::{
|
||||
self,
|
||||
convert::{TryFrom, TryInto},
|
||||
@@ -459,10 +459,10 @@ pub trait Hash:
|
||||
}
|
||||
|
||||
/// The ordered Patricia tree root of the given `input`.
|
||||
fn ordered_trie_root(input: Vec<Vec<u8>>) -> Self::Output;
|
||||
fn ordered_trie_root(input: Vec<Vec<u8>>, state_version: StateVersion) -> Self::Output;
|
||||
|
||||
/// The Patricia tree root of the given mapping.
|
||||
fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> Self::Output;
|
||||
fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>, state_version: StateVersion) -> Self::Output;
|
||||
}
|
||||
|
||||
/// Blake2-256 Hash implementation.
|
||||
@@ -483,12 +483,12 @@ impl Hasher for BlakeTwo256 {
|
||||
impl Hash for BlakeTwo256 {
|
||||
type Output = sp_core::H256;
|
||||
|
||||
fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> Self::Output {
|
||||
sp_io::trie::blake2_256_root(input)
|
||||
fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>, version: StateVersion) -> Self::Output {
|
||||
sp_io::trie::blake2_256_root(input, version)
|
||||
}
|
||||
|
||||
fn ordered_trie_root(input: Vec<Vec<u8>>) -> Self::Output {
|
||||
sp_io::trie::blake2_256_ordered_root(input)
|
||||
fn ordered_trie_root(input: Vec<Vec<u8>>, version: StateVersion) -> Self::Output {
|
||||
sp_io::trie::blake2_256_ordered_root(input, version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,12 +510,12 @@ impl Hasher for Keccak256 {
|
||||
impl Hash for Keccak256 {
|
||||
type Output = sp_core::H256;
|
||||
|
||||
fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> Self::Output {
|
||||
sp_io::trie::keccak_256_root(input)
|
||||
fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>, version: StateVersion) -> Self::Output {
|
||||
sp_io::trie::keccak_256_root(input, version)
|
||||
}
|
||||
|
||||
fn ordered_trie_root(input: Vec<Vec<u8>>) -> Self::Output {
|
||||
sp_io::trie::keccak_256_ordered_root(input)
|
||||
fn ordered_trie_root(input: Vec<Vec<u8>>, version: StateVersion) -> Self::Output {
|
||||
sp_io::trie::keccak_256_ordered_root(input, version)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ log = { version = "0.4.11", optional = true }
|
||||
thiserror = { version = "1.0.30", optional = true }
|
||||
parking_lot = { version = "0.11.2", optional = true }
|
||||
hash-db = { version = "0.15.2", default-features = false }
|
||||
trie-db = { version = "0.22.6", default-features = false }
|
||||
trie-root = { version = "0.16.0", default-features = false }
|
||||
trie-db = { version = "0.23.0", default-features = false }
|
||||
trie-root = { version = "0.17.0", default-features = false }
|
||||
sp-trie = { version = "4.0.0", path = "../trie", default-features = false }
|
||||
sp-core = { version = "4.1.0-dev", path = "../core", default-features = false }
|
||||
sp-panic-handler = { version = "4.0.0", path = "../panic-handler", optional = true }
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
};
|
||||
use codec::Encode;
|
||||
use hash_db::Hasher;
|
||||
use sp_core::storage::{ChildInfo, TrackedStorageKey};
|
||||
use sp_core::storage::{ChildInfo, StateVersion, TrackedStorageKey};
|
||||
#[cfg(feature = "std")]
|
||||
use sp_core::traits::RuntimeCode;
|
||||
use sp_std::vec::Vec;
|
||||
@@ -140,6 +140,7 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (H::Out, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord;
|
||||
@@ -151,6 +152,7 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (H::Out, bool, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord;
|
||||
@@ -176,7 +178,6 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
fn as_trie_backend(&self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Calculate the storage root, with given delta over what is already stored
|
||||
/// in the backend, and produce a "transaction" that can be used to commit.
|
||||
/// Does include child storage updates.
|
||||
@@ -186,6 +187,7 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
child_deltas: impl Iterator<
|
||||
Item = (&'a ChildInfo, impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>),
|
||||
>,
|
||||
state_version: StateVersion,
|
||||
) -> (H::Out, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord + Encode,
|
||||
@@ -194,7 +196,8 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
let mut child_roots: Vec<_> = Default::default();
|
||||
// child first
|
||||
for (child_info, child_delta) in child_deltas {
|
||||
let (child_root, empty, child_txs) = self.child_storage_root(&child_info, child_delta);
|
||||
let (child_root, empty, child_txs) =
|
||||
self.child_storage_root(&child_info, child_delta, state_version);
|
||||
let prefixed_storage_key = child_info.prefixed_storage_key();
|
||||
txs.consolidate(child_txs);
|
||||
if empty {
|
||||
@@ -205,8 +208,9 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
|
||||
}
|
||||
let (root, parent_txs) = self.storage_root(
|
||||
delta
|
||||
.map(|(k, v)| (k, v.as_ref().map(|v| &v[..])))
|
||||
.map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..])))
|
||||
.chain(child_roots.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..])))),
|
||||
state_version,
|
||||
);
|
||||
txs.consolidate(parent_txs);
|
||||
(root, txs)
|
||||
@@ -286,7 +290,11 @@ impl Consolidate for Vec<(Option<ChildInfo>, StorageCollection)> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher, KF: sp_trie::KeyFunction<H>> Consolidate for sp_trie::GenericMemoryDB<H, KF> {
|
||||
impl<H, KF> Consolidate for sp_trie::GenericMemoryDB<H, KF>
|
||||
where
|
||||
H: Hasher,
|
||||
KF: sp_trie::KeyFunction<H>,
|
||||
{
|
||||
fn consolidate(&mut self, other: Self) {
|
||||
sp_trie::GenericMemoryDB::consolidate(self, other)
|
||||
}
|
||||
|
||||
@@ -23,13 +23,14 @@ use hash_db::Hasher;
|
||||
use log::warn;
|
||||
use sp_core::{
|
||||
storage::{
|
||||
well_known_keys::is_child_storage_key, ChildInfo, Storage, StorageChild, TrackedStorageKey,
|
||||
well_known_keys::is_child_storage_key, ChildInfo, StateVersion, Storage, StorageChild,
|
||||
TrackedStorageKey,
|
||||
},
|
||||
traits::Externalities,
|
||||
Blake2Hasher,
|
||||
};
|
||||
use sp_externalities::{Extension, Extensions};
|
||||
use sp_trie::{empty_child_trie_root, trie_types::Layout, TrieConfiguration};
|
||||
use sp_trie::{empty_child_trie_root, LayoutV0, LayoutV1, TrieConfiguration};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
collections::BTreeMap,
|
||||
@@ -273,7 +274,7 @@ impl Externalities for BasicExternalities {
|
||||
crate::ext::StorageAppend::new(current).append(value);
|
||||
}
|
||||
|
||||
fn storage_root(&mut self) -> Vec<u8> {
|
||||
fn storage_root(&mut self, state_version: StateVersion) -> Vec<u8> {
|
||||
let mut top = self.inner.top.clone();
|
||||
let prefixed_keys: Vec<_> = self
|
||||
.inner
|
||||
@@ -284,9 +285,9 @@ impl Externalities for BasicExternalities {
|
||||
// Single child trie implementation currently allows using the same child
|
||||
// empty root for all child trie. Using null storage key until multiple
|
||||
// type of child trie support.
|
||||
let empty_hash = empty_child_trie_root::<Layout<Blake2Hasher>>();
|
||||
let empty_hash = empty_child_trie_root::<LayoutV1<Blake2Hasher>>();
|
||||
for (prefixed_storage_key, child_info) in prefixed_keys {
|
||||
let child_root = self.child_storage_root(&child_info);
|
||||
let child_root = self.child_storage_root(&child_info, state_version);
|
||||
if &empty_hash[..] == &child_root[..] {
|
||||
top.remove(prefixed_storage_key.as_slice());
|
||||
} else {
|
||||
@@ -294,17 +295,26 @@ impl Externalities for BasicExternalities {
|
||||
}
|
||||
}
|
||||
|
||||
Layout::<Blake2Hasher>::trie_root(self.inner.top.clone()).as_ref().into()
|
||||
match state_version {
|
||||
StateVersion::V0 =>
|
||||
LayoutV0::<Blake2Hasher>::trie_root(self.inner.top.clone()).as_ref().into(),
|
||||
StateVersion::V1 =>
|
||||
LayoutV1::<Blake2Hasher>::trie_root(self.inner.top.clone()).as_ref().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn child_storage_root(&mut self, child_info: &ChildInfo) -> Vec<u8> {
|
||||
fn child_storage_root(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
state_version: StateVersion,
|
||||
) -> Vec<u8> {
|
||||
if let Some(child) = self.inner.children_default.get(child_info.storage_key()) {
|
||||
let delta = child.data.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref())));
|
||||
crate::in_memory_backend::new_in_mem::<Blake2Hasher>()
|
||||
.child_storage_root(&child.child_info, delta)
|
||||
.child_storage_root(&child.child_info, delta, state_version)
|
||||
.0
|
||||
} else {
|
||||
empty_child_trie_root::<Layout<Blake2Hasher>>()
|
||||
empty_child_trie_root::<LayoutV1<Blake2Hasher>>()
|
||||
}
|
||||
.encode()
|
||||
}
|
||||
@@ -389,7 +399,7 @@ mod tests {
|
||||
const ROOT: [u8; 32] =
|
||||
hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
|
||||
|
||||
assert_eq!(&ext.storage_root()[..], &ROOT);
|
||||
assert_eq!(&ext.storage_root(StateVersion::default())[..], &ROOT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -24,9 +24,11 @@ use codec::{Decode, Encode, EncodeAppend};
|
||||
use hash_db::Hasher;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_core::hexdisplay::HexDisplay;
|
||||
use sp_core::storage::{well_known_keys::is_child_storage_key, ChildInfo, TrackedStorageKey};
|
||||
use sp_core::storage::{
|
||||
well_known_keys::is_child_storage_key, ChildInfo, StateVersion, TrackedStorageKey,
|
||||
};
|
||||
use sp_externalities::{Extension, ExtensionStore, Externalities};
|
||||
use sp_trie::{empty_child_trie_root, trie_types::Layout};
|
||||
use sp_trie::{empty_child_trie_root, LayoutV1};
|
||||
|
||||
use crate::{log_error, trace, warn, StorageTransactionCache};
|
||||
use sp_std::{
|
||||
@@ -505,7 +507,7 @@ where
|
||||
StorageAppend::new(current_value).append(value);
|
||||
}
|
||||
|
||||
fn storage_root(&mut self) -> Vec<u8> {
|
||||
fn storage_root(&mut self, state_version: StateVersion) -> Vec<u8> {
|
||||
let _guard = guard();
|
||||
if let Some(ref root) = self.storage_transaction_cache.transaction_storage_root {
|
||||
trace!(
|
||||
@@ -518,7 +520,9 @@ where
|
||||
return root.encode()
|
||||
}
|
||||
|
||||
let root = self.overlay.storage_root(self.backend, self.storage_transaction_cache);
|
||||
let root =
|
||||
self.overlay
|
||||
.storage_root(self.backend, self.storage_transaction_cache, state_version);
|
||||
trace!(
|
||||
target: "state",
|
||||
method = "StorageRoot",
|
||||
@@ -529,7 +533,11 @@ where
|
||||
root.encode()
|
||||
}
|
||||
|
||||
fn child_storage_root(&mut self, child_info: &ChildInfo) -> Vec<u8> {
|
||||
fn child_storage_root(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
state_version: StateVersion,
|
||||
) -> Vec<u8> {
|
||||
let _guard = guard();
|
||||
let storage_key = child_info.storage_key();
|
||||
let prefixed_storage_key = child_info.prefixed_storage_key();
|
||||
@@ -537,7 +545,8 @@ where
|
||||
let root = self
|
||||
.storage(prefixed_storage_key.as_slice())
|
||||
.and_then(|k| Decode::decode(&mut &k[..]).ok())
|
||||
.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>());
|
||||
// V1 is equivalent to V0 on empty root.
|
||||
.unwrap_or_else(|| empty_child_trie_root::<LayoutV1<H>>());
|
||||
trace!(
|
||||
target: "state",
|
||||
method = "ChildStorageRoot",
|
||||
@@ -550,7 +559,7 @@ where
|
||||
} else {
|
||||
let root = if let Some((changes, info)) = self.overlay.child_changes(storage_key) {
|
||||
let delta = changes.map(|(k, v)| (k.as_ref(), v.value().map(AsRef::as_ref)));
|
||||
Some(self.backend.child_storage_root(info, delta))
|
||||
Some(self.backend.child_storage_root(info, delta, state_version))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -583,7 +592,8 @@ where
|
||||
let root = self
|
||||
.storage(prefixed_storage_key.as_slice())
|
||||
.and_then(|k| Decode::decode(&mut &k[..]).ok())
|
||||
.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>());
|
||||
// V1 is equivalent to V0 on empty root.
|
||||
.unwrap_or_else(|| empty_child_trie_root::<LayoutV1<H>>());
|
||||
|
||||
trace!(
|
||||
target: "state",
|
||||
@@ -648,7 +658,12 @@ where
|
||||
self.overlay.rollback_transaction().expect(BENCHMARKING_FN);
|
||||
}
|
||||
self.overlay
|
||||
.drain_storage_changes(self.backend, Default::default(), self.storage_transaction_cache)
|
||||
.drain_storage_changes(
|
||||
self.backend,
|
||||
Default::default(),
|
||||
self.storage_transaction_cache,
|
||||
Default::default(), // using any state
|
||||
)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.backend.wipe().expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.mark_dirty();
|
||||
@@ -658,12 +673,19 @@ where
|
||||
}
|
||||
|
||||
fn commit(&mut self) {
|
||||
// Bench always use latest state.
|
||||
let state_version = StateVersion::default();
|
||||
for _ in 0..self.overlay.transaction_depth() {
|
||||
self.overlay.commit_transaction().expect(BENCHMARKING_FN);
|
||||
}
|
||||
let changes = self
|
||||
.overlay
|
||||
.drain_storage_changes(self.backend, Default::default(), self.storage_transaction_cache)
|
||||
.drain_storage_changes(
|
||||
self.backend,
|
||||
Default::default(),
|
||||
self.storage_transaction_cache,
|
||||
state_version,
|
||||
)
|
||||
.expect(EXT_NOT_ALLOWED_TO_FAIL);
|
||||
self.backend
|
||||
.commit(
|
||||
@@ -878,15 +900,18 @@ mod tests {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
overlay.set_storage(vec![20], None);
|
||||
overlay.set_storage(vec![30], Some(vec![31]));
|
||||
let backend = Storage {
|
||||
top: map![
|
||||
vec![10] => vec![10],
|
||||
vec![20] => vec![20],
|
||||
vec![40] => vec![40]
|
||||
],
|
||||
children_default: map![],
|
||||
}
|
||||
.into();
|
||||
let backend = (
|
||||
Storage {
|
||||
top: map![
|
||||
vec![10] => vec![10],
|
||||
vec![20] => vec![20],
|
||||
vec![40] => vec![40]
|
||||
],
|
||||
children_default: map![],
|
||||
},
|
||||
StateVersion::default(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
|
||||
|
||||
@@ -924,13 +949,16 @@ mod tests {
|
||||
overlay.set_storage(vec![27], None);
|
||||
overlay.set_storage(vec![28], None);
|
||||
overlay.set_storage(vec![29], None);
|
||||
let backend = Storage {
|
||||
top: map![
|
||||
vec![30] => vec![30]
|
||||
],
|
||||
children_default: map![],
|
||||
}
|
||||
.into();
|
||||
let backend = (
|
||||
Storage {
|
||||
top: map![
|
||||
vec![30] => vec![30]
|
||||
],
|
||||
children_default: map![],
|
||||
},
|
||||
StateVersion::default(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
|
||||
|
||||
@@ -948,20 +976,23 @@ mod tests {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
overlay.set_child_storage(child_info, vec![20], None);
|
||||
overlay.set_child_storage(child_info, vec![30], Some(vec![31]));
|
||||
let backend = Storage {
|
||||
top: map![],
|
||||
children_default: map![
|
||||
child_info.storage_key().to_vec() => StorageChild {
|
||||
data: map![
|
||||
vec![10] => vec![10],
|
||||
vec![20] => vec![20],
|
||||
vec![40] => vec![40]
|
||||
],
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
}
|
||||
.into();
|
||||
let backend = (
|
||||
Storage {
|
||||
top: map![],
|
||||
children_default: map![
|
||||
child_info.storage_key().to_vec() => StorageChild {
|
||||
data: map![
|
||||
vec![10] => vec![10],
|
||||
vec![20] => vec![20],
|
||||
vec![40] => vec![40]
|
||||
],
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
},
|
||||
StateVersion::default(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
|
||||
|
||||
@@ -993,20 +1024,23 @@ mod tests {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
overlay.set_child_storage(child_info, vec![20], None);
|
||||
overlay.set_child_storage(child_info, vec![30], Some(vec![31]));
|
||||
let backend = Storage {
|
||||
top: map![],
|
||||
children_default: map![
|
||||
child_info.storage_key().to_vec() => StorageChild {
|
||||
data: map![
|
||||
vec![10] => vec![10],
|
||||
vec![20] => vec![20],
|
||||
vec![30] => vec![40]
|
||||
],
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
}
|
||||
.into();
|
||||
let backend = (
|
||||
Storage {
|
||||
top: map![],
|
||||
children_default: map![
|
||||
child_info.storage_key().to_vec() => StorageChild {
|
||||
data: map![
|
||||
vec![10] => vec![10],
|
||||
vec![20] => vec![20],
|
||||
vec![30] => vec![40]
|
||||
],
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
},
|
||||
StateVersion::default(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
|
||||
|
||||
@@ -1032,18 +1066,21 @@ mod tests {
|
||||
let child_info = &child_info;
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let backend = Storage {
|
||||
top: map![],
|
||||
children_default: map![
|
||||
child_info.storage_key().to_vec() => StorageChild {
|
||||
data: map![
|
||||
vec![30] => vec![40]
|
||||
],
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
}
|
||||
.into();
|
||||
let backend = (
|
||||
Storage {
|
||||
top: map![],
|
||||
children_default: map![
|
||||
child_info.storage_key().to_vec() => StorageChild {
|
||||
data: map![
|
||||
vec![30] => vec![40]
|
||||
],
|
||||
child_info: child_info.to_owned(),
|
||||
}
|
||||
],
|
||||
},
|
||||
StateVersion::default(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let ext = TestExt::new(&mut overlay, &mut cache, &backend, None);
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ use crate::{
|
||||
};
|
||||
use codec::Codec;
|
||||
use hash_db::Hasher;
|
||||
use sp_core::storage::{ChildInfo, Storage};
|
||||
use sp_trie::{empty_trie_root, Layout, MemoryDB};
|
||||
use sp_core::storage::{ChildInfo, StateVersion, Storage};
|
||||
use sp_trie::{empty_trie_root, LayoutV1, MemoryDB};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
/// Create a new empty instance of in-memory backend.
|
||||
@@ -32,7 +32,8 @@ where
|
||||
H::Out: Codec + Ord,
|
||||
{
|
||||
let db = MemoryDB::default();
|
||||
TrieBackend::new(db, empty_trie_root::<Layout<H>>())
|
||||
// V1 is same as V0 for an empty trie.
|
||||
TrieBackend::new(db, empty_trie_root::<LayoutV1<H>>())
|
||||
}
|
||||
|
||||
impl<H: Hasher> TrieBackend<MemoryDB<H>, H>
|
||||
@@ -43,9 +44,10 @@ where
|
||||
pub fn update<T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>>(
|
||||
&self,
|
||||
changes: T,
|
||||
state_version: StateVersion,
|
||||
) -> Self {
|
||||
let mut clone = self.clone();
|
||||
clone.insert(changes);
|
||||
clone.insert(changes, state_version);
|
||||
clone
|
||||
}
|
||||
|
||||
@@ -53,6 +55,7 @@ where
|
||||
pub fn insert<T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>>(
|
||||
&mut self,
|
||||
changes: T,
|
||||
state_version: StateVersion,
|
||||
) {
|
||||
let (top, child) = changes.into_iter().partition::<Vec<_>, _>(|v| v.0.is_none());
|
||||
let (root, transaction) = self.full_storage_root(
|
||||
@@ -60,6 +63,7 @@ where
|
||||
child.iter().filter_map(|v| {
|
||||
v.0.as_ref().map(|c| (c, v.1.iter().map(|(k, v)| (&k[..], v.as_deref()))))
|
||||
}),
|
||||
state_version,
|
||||
);
|
||||
|
||||
self.apply_transaction(root, transaction);
|
||||
@@ -103,53 +107,63 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> From<HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>>
|
||||
impl<H: Hasher> From<(HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>, StateVersion)>
|
||||
for TrieBackend<MemoryDB<H>, H>
|
||||
where
|
||||
H::Out: Codec + Ord,
|
||||
{
|
||||
fn from(inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>) -> Self {
|
||||
fn from(
|
||||
(inner, state_version): (
|
||||
HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>,
|
||||
StateVersion,
|
||||
),
|
||||
) -> Self {
|
||||
let mut backend = new_in_mem();
|
||||
backend.insert(
|
||||
inner
|
||||
.into_iter()
|
||||
.map(|(k, m)| (k, m.into_iter().map(|(k, v)| (k, Some(v))).collect())),
|
||||
state_version,
|
||||
);
|
||||
backend
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> From<Storage> for TrieBackend<MemoryDB<H>, H>
|
||||
impl<H: Hasher> From<(Storage, StateVersion)> for TrieBackend<MemoryDB<H>, H>
|
||||
where
|
||||
H::Out: Codec + Ord,
|
||||
{
|
||||
fn from(inners: Storage) -> Self {
|
||||
fn from((inners, state_version): (Storage, StateVersion)) -> Self {
|
||||
let mut inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>> = inners
|
||||
.children_default
|
||||
.into_iter()
|
||||
.map(|(_k, c)| (Some(c.child_info), c.data))
|
||||
.collect();
|
||||
inner.insert(None, inners.top);
|
||||
inner.into()
|
||||
(inner, state_version).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> From<BTreeMap<StorageKey, StorageValue>> for TrieBackend<MemoryDB<H>, H>
|
||||
impl<H: Hasher> From<(BTreeMap<StorageKey, StorageValue>, StateVersion)>
|
||||
for TrieBackend<MemoryDB<H>, H>
|
||||
where
|
||||
H::Out: Codec + Ord,
|
||||
{
|
||||
fn from(inner: BTreeMap<StorageKey, StorageValue>) -> Self {
|
||||
fn from((inner, state_version): (BTreeMap<StorageKey, StorageValue>, StateVersion)) -> Self {
|
||||
let mut expanded = HashMap::new();
|
||||
expanded.insert(None, inner);
|
||||
expanded.into()
|
||||
(expanded, state_version).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> From<Vec<(Option<ChildInfo>, StorageCollection)>> for TrieBackend<MemoryDB<H>, H>
|
||||
impl<H: Hasher> From<(Vec<(Option<ChildInfo>, StorageCollection)>, StateVersion)>
|
||||
for TrieBackend<MemoryDB<H>, H>
|
||||
where
|
||||
H::Out: Codec + Ord,
|
||||
{
|
||||
fn from(inner: Vec<(Option<ChildInfo>, StorageCollection)>) -> Self {
|
||||
fn from(
|
||||
(inner, state_version): (Vec<(Option<ChildInfo>, StorageCollection)>, StateVersion),
|
||||
) -> Self {
|
||||
let mut expanded: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>> =
|
||||
HashMap::new();
|
||||
for (child_info, key_values) in inner {
|
||||
@@ -160,7 +174,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
expanded.into()
|
||||
(expanded, state_version).into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,16 +182,20 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::backend::Backend;
|
||||
use sp_core::storage::StateVersion;
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
|
||||
/// Assert in memory backend with only child trie keys works as trie backend.
|
||||
#[test]
|
||||
fn in_memory_with_child_trie_only() {
|
||||
let state_version = StateVersion::default();
|
||||
let storage = new_in_mem::<BlakeTwo256>();
|
||||
let child_info = ChildInfo::new_default(b"1");
|
||||
let child_info = &child_info;
|
||||
let storage = storage
|
||||
.update(vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])]);
|
||||
let storage = storage.update(
|
||||
vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])],
|
||||
state_version,
|
||||
);
|
||||
let trie_backend = storage.as_trie_backend().unwrap();
|
||||
assert_eq!(trie_backend.child_storage(child_info, b"2").unwrap(), Some(b"3".to_vec()));
|
||||
let storage_key = child_info.prefixed_storage_key();
|
||||
@@ -186,13 +204,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn insert_multiple_times_child_data_works() {
|
||||
let state_version = StateVersion::default();
|
||||
let mut storage = new_in_mem::<BlakeTwo256>();
|
||||
let child_info = ChildInfo::new_default(b"1");
|
||||
|
||||
storage
|
||||
.insert(vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])]);
|
||||
storage
|
||||
.insert(vec![(Some(child_info.clone()), vec![(b"1".to_vec(), Some(b"3".to_vec()))])]);
|
||||
storage.insert(
|
||||
vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])],
|
||||
state_version,
|
||||
);
|
||||
storage.insert(
|
||||
vec![(Some(child_info.clone()), vec![(b"1".to_vec(), Some(b"3".to_vec()))])],
|
||||
state_version,
|
||||
);
|
||||
|
||||
assert_eq!(storage.child_storage(&child_info, &b"2"[..]), Ok(Some(b"3".to_vec())));
|
||||
assert_eq!(storage.child_storage(&child_info, &b"1"[..]), Ok(Some(b"3".to_vec())));
|
||||
|
||||
@@ -126,6 +126,7 @@ impl sp_std::fmt::Display for DefaultError {
|
||||
|
||||
pub use crate::{
|
||||
backend::Backend,
|
||||
error::{Error, ExecutionError},
|
||||
ext::Ext,
|
||||
overlayed_changes::{
|
||||
ChildStorageCollection, IndexOperation, OffchainChangesCollection,
|
||||
@@ -136,7 +137,6 @@ pub use crate::{
|
||||
trie_backend::TrieBackend,
|
||||
trie_backend_essence::{Storage, TrieBackendStorage},
|
||||
};
|
||||
pub use error::{Error, ExecutionError};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod std_reexport {
|
||||
@@ -151,8 +151,8 @@ mod std_reexport {
|
||||
testing::TestExternalities,
|
||||
};
|
||||
pub use sp_trie::{
|
||||
trie_types::{Layout, TrieDBMut},
|
||||
CompactProof, DBValue, MemoryDB, StorageProof, TrieMut,
|
||||
trie_types::{TrieDBMutV0, TrieDBMutV1},
|
||||
CompactProof, DBValue, LayoutV0, LayoutV1, MemoryDB, StorageProof, TrieMut,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1353,7 +1353,7 @@ mod tests {
|
||||
use codec::{Decode, Encode};
|
||||
use sp_core::{
|
||||
map,
|
||||
storage::ChildInfo,
|
||||
storage::{ChildInfo, StateVersion},
|
||||
testing::TaskExecutor,
|
||||
traits::{CodeExecutor, Externalities, RuntimeCode},
|
||||
NativeOrEncoded, NeverNativeValue,
|
||||
@@ -1416,7 +1416,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn execute_works() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
execute_works_inner(StateVersion::V0);
|
||||
execute_works_inner(StateVersion::V1);
|
||||
}
|
||||
fn execute_works_inner(state_version: StateVersion) {
|
||||
let backend = trie_backend::tests::test_trie(state_version);
|
||||
let mut overlayed_changes = Default::default();
|
||||
let wasm_code = RuntimeCode::empty();
|
||||
|
||||
@@ -1440,7 +1444,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn execute_works_with_native_else_wasm() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
execute_works_with_native_else_wasm_inner(StateVersion::V0);
|
||||
execute_works_with_native_else_wasm_inner(StateVersion::V1);
|
||||
}
|
||||
fn execute_works_with_native_else_wasm_inner(state_version: StateVersion) {
|
||||
let backend = trie_backend::tests::test_trie(state_version);
|
||||
let mut overlayed_changes = Default::default();
|
||||
let wasm_code = RuntimeCode::empty();
|
||||
|
||||
@@ -1464,8 +1472,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn dual_execution_strategy_detects_consensus_failure() {
|
||||
dual_execution_strategy_detects_consensus_failure_inner(StateVersion::V0);
|
||||
dual_execution_strategy_detects_consensus_failure_inner(StateVersion::V1);
|
||||
}
|
||||
fn dual_execution_strategy_detects_consensus_failure_inner(state_version: StateVersion) {
|
||||
let mut consensus_failed = false;
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let backend = trie_backend::tests::test_trie(state_version);
|
||||
let mut overlayed_changes = Default::default();
|
||||
let wasm_code = RuntimeCode::empty();
|
||||
|
||||
@@ -1498,6 +1510,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn prove_execution_and_proof_check_works() {
|
||||
prove_execution_and_proof_check_works_inner(StateVersion::V0);
|
||||
prove_execution_and_proof_check_works_inner(StateVersion::V1);
|
||||
}
|
||||
fn prove_execution_and_proof_check_works_inner(state_version: StateVersion) {
|
||||
let executor = DummyCodeExecutor {
|
||||
native_available: true,
|
||||
native_succeeds: true,
|
||||
@@ -1505,8 +1521,8 @@ mod tests {
|
||||
};
|
||||
|
||||
// fetch execution proof from 'remote' full node
|
||||
let mut remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
|
||||
let mut remote_backend = trie_backend::tests::test_trie(state_version);
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty(), state_version).0;
|
||||
let (remote_result, remote_proof) = prove_execution(
|
||||
&mut remote_backend,
|
||||
&mut Default::default(),
|
||||
@@ -1544,7 +1560,7 @@ mod tests {
|
||||
b"abc".to_vec() => b"2".to_vec(),
|
||||
b"bbb".to_vec() => b"3".to_vec()
|
||||
];
|
||||
let state = InMemoryBackend::<BlakeTwo256>::from(initial);
|
||||
let state = InMemoryBackend::<BlakeTwo256>::from((initial, StateVersion::default()));
|
||||
let backend = state.as_trie_backend().unwrap();
|
||||
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
@@ -1613,7 +1629,7 @@ mod tests {
|
||||
b"d".to_vec() => b"3".to_vec()
|
||||
],
|
||||
];
|
||||
let backend = InMemoryBackend::<BlakeTwo256>::from(initial);
|
||||
let backend = InMemoryBackend::<BlakeTwo256>::from((initial, StateVersion::default()));
|
||||
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
overlay.set_child_storage(&child_info, b"1".to_vec(), Some(b"1312".to_vec()));
|
||||
@@ -1655,7 +1671,7 @@ mod tests {
|
||||
b"d".to_vec() => b"3".to_vec()
|
||||
],
|
||||
];
|
||||
let backend = InMemoryBackend::<BlakeTwo256>::from(initial);
|
||||
let backend = InMemoryBackend::<BlakeTwo256>::from((initial, StateVersion::default()));
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let mut ext = Ext::new(&mut overlay, &mut cache, &backend, None);
|
||||
@@ -1789,13 +1805,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn prove_read_and_proof_check_works() {
|
||||
prove_read_and_proof_check_works_inner(StateVersion::V0);
|
||||
prove_read_and_proof_check_works_inner(StateVersion::V1);
|
||||
}
|
||||
fn prove_read_and_proof_check_works_inner(state_version: StateVersion) {
|
||||
let child_info = ChildInfo::new_default(b"sub1");
|
||||
let missing_child_info = ChildInfo::new_default(b"sub1sub2"); // key will include other child root to proof.
|
||||
let child_info = &child_info;
|
||||
let missing_child_info = &missing_child_info;
|
||||
// fetch read proof from 'remote' full node
|
||||
let remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
|
||||
let remote_backend = trie_backend::tests::test_trie(state_version);
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty(), state_version).0;
|
||||
let remote_proof = prove_read(remote_backend, &[b"value2"]).unwrap();
|
||||
let remote_proof = test_compact(remote_proof, &remote_root);
|
||||
// check proof locally
|
||||
@@ -1812,8 +1832,8 @@ mod tests {
|
||||
);
|
||||
assert_eq!(local_result2, false);
|
||||
// on child trie
|
||||
let remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
|
||||
let remote_backend = trie_backend::tests::test_trie(state_version);
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty(), state_version).0;
|
||||
let remote_proof = prove_child_read(remote_backend, child_info, &[b"value3"]).unwrap();
|
||||
let remote_proof = test_compact(remote_proof, &remote_root);
|
||||
let local_result1 = read_child_proof_check::<BlakeTwo256, _>(
|
||||
@@ -1877,7 +1897,8 @@ mod tests {
|
||||
storage.insert(Some(child_info), items);
|
||||
}
|
||||
|
||||
let trie: InMemoryBackend<BlakeTwo256> = storage.clone().into();
|
||||
let trie: InMemoryBackend<BlakeTwo256> =
|
||||
(storage.clone(), StateVersion::default()).into();
|
||||
let trie_root = trie.root().clone();
|
||||
let backend = crate::ProvingBackend::new(&trie);
|
||||
let mut queries = Vec::new();
|
||||
@@ -1940,15 +1961,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn prove_read_with_size_limit_works() {
|
||||
let remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_root = remote_backend.storage_root(::std::iter::empty()).0;
|
||||
let state_version = StateVersion::V0;
|
||||
let remote_backend = trie_backend::tests::test_trie(state_version);
|
||||
let remote_root = remote_backend.storage_root(::std::iter::empty(), state_version).0;
|
||||
let (proof, count) =
|
||||
prove_range_read_with_size(remote_backend, None, None, 0, None).unwrap();
|
||||
// Always contains at least some nodes.
|
||||
assert_eq!(proof.into_memory_db::<BlakeTwo256>().drain().len(), 3);
|
||||
assert_eq!(count, 1);
|
||||
|
||||
let remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_backend = trie_backend::tests::test_trie(state_version);
|
||||
let (proof, count) =
|
||||
prove_range_read_with_size(remote_backend, None, None, 800, Some(&[])).unwrap();
|
||||
assert_eq!(proof.clone().into_memory_db::<BlakeTwo256>().drain().len(), 9);
|
||||
@@ -1971,7 +1993,7 @@ mod tests {
|
||||
assert_eq!(results.len() as u32, 101);
|
||||
assert_eq!(completed, false);
|
||||
|
||||
let remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_backend = trie_backend::tests::test_trie(state_version);
|
||||
let (proof, count) =
|
||||
prove_range_read_with_size(remote_backend, None, None, 50000, Some(&[])).unwrap();
|
||||
assert_eq!(proof.clone().into_memory_db::<BlakeTwo256>().drain().len(), 11);
|
||||
@@ -1989,10 +2011,66 @@ mod tests {
|
||||
assert_eq!(completed, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_state_versioning_switch_proofs() {
|
||||
let mut state_version = StateVersion::V0;
|
||||
let (mut mdb, mut root) = trie_backend::tests::test_db(state_version);
|
||||
{
|
||||
let mut trie = TrieDBMutV0::from_existing(&mut mdb, &mut root).unwrap();
|
||||
trie.insert(b"foo", vec![1u8; 1_000].as_slice()) // big inner hash
|
||||
.expect("insert failed");
|
||||
trie.insert(b"foo2", vec![3u8; 16].as_slice()) // no inner hash
|
||||
.expect("insert failed");
|
||||
trie.insert(b"foo222", vec![5u8; 100].as_slice()) // inner hash
|
||||
.expect("insert failed");
|
||||
}
|
||||
|
||||
let check_proof = |mdb, root, state_version| -> StorageProof {
|
||||
let remote_backend = TrieBackend::new(mdb, root);
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty(), state_version).0;
|
||||
let remote_proof = prove_read(remote_backend, &[b"foo222"]).unwrap();
|
||||
// check proof locally
|
||||
let local_result1 =
|
||||
read_proof_check::<BlakeTwo256, _>(remote_root, remote_proof.clone(), &[b"foo222"])
|
||||
.unwrap();
|
||||
// check that results are correct
|
||||
assert_eq!(
|
||||
local_result1.into_iter().collect::<Vec<_>>(),
|
||||
vec![(b"foo222".to_vec(), Some(vec![5u8; 100]))],
|
||||
);
|
||||
remote_proof
|
||||
};
|
||||
|
||||
let remote_proof = check_proof(mdb.clone(), root.clone(), state_version);
|
||||
// check full values in proof
|
||||
assert!(remote_proof.encode().len() > 1_100);
|
||||
assert!(remote_proof.encoded_size() > 1_100);
|
||||
let root1 = root.clone();
|
||||
|
||||
// do switch
|
||||
state_version = StateVersion::V1;
|
||||
{
|
||||
let mut trie = TrieDBMutV1::from_existing(&mut mdb, &mut root).unwrap();
|
||||
trie.insert(b"foo222", vec![5u8; 100].as_slice()) // inner hash
|
||||
.expect("insert failed");
|
||||
// update with same value do change
|
||||
trie.insert(b"foo", vec![1u8; 1000].as_slice()) // inner hash
|
||||
.expect("insert failed");
|
||||
}
|
||||
let root3 = root.clone();
|
||||
assert!(root1 != root3);
|
||||
let remote_proof = check_proof(mdb.clone(), root.clone(), state_version);
|
||||
// nodes foo is replaced by its hashed value form.
|
||||
assert!(remote_proof.encode().len() < 1000);
|
||||
assert!(remote_proof.encoded_size() < 1000);
|
||||
assert_eq!(remote_proof.encode().len(), remote_proof.encoded_size());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prove_range_with_child_works() {
|
||||
let remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_root = remote_backend.storage_root(::std::iter::empty()).0;
|
||||
let state_version = StateVersion::V0;
|
||||
let remote_backend = trie_backend::tests::test_trie(state_version);
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty(), state_version).0;
|
||||
let mut start_at = smallvec::SmallVec::<[Vec<u8>; 2]>::new();
|
||||
let trie_backend = remote_backend.as_trie_backend().unwrap();
|
||||
let max_iter = 1000;
|
||||
@@ -2030,20 +2108,34 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compact_multiple_child_trie() {
|
||||
let size_no_inner_hash = compact_multiple_child_trie_inner(StateVersion::V0);
|
||||
let size_inner_hash = compact_multiple_child_trie_inner(StateVersion::V1);
|
||||
assert!(size_inner_hash < size_no_inner_hash);
|
||||
}
|
||||
fn compact_multiple_child_trie_inner(state_version: StateVersion) -> usize {
|
||||
// this root will be queried
|
||||
let child_info1 = ChildInfo::new_default(b"sub1");
|
||||
// this root will not be include in proof
|
||||
let child_info2 = ChildInfo::new_default(b"sub2");
|
||||
// this root will be include in proof
|
||||
let child_info3 = ChildInfo::new_default(b"sub");
|
||||
let remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_backend = trie_backend::tests::test_trie(state_version);
|
||||
let long_vec: Vec<u8> = (0..1024usize).map(|_| 8u8).collect();
|
||||
let (remote_root, transaction) = remote_backend.full_storage_root(
|
||||
std::iter::empty(),
|
||||
vec![
|
||||
(
|
||||
&child_info1,
|
||||
vec![(&b"key1"[..], Some(&b"val2"[..])), (&b"key2"[..], Some(&b"val3"[..]))]
|
||||
.into_iter(),
|
||||
vec![
|
||||
// a inner hashable node
|
||||
(&b"k"[..], Some(&long_vec[..])),
|
||||
// need to ensure this is not an inline node
|
||||
// otherwhise we do not know what is accessed when
|
||||
// storing proof.
|
||||
(&b"key1"[..], Some(&vec![5u8; 32][..])),
|
||||
(&b"key2"[..], Some(&b"val3"[..])),
|
||||
]
|
||||
.into_iter(),
|
||||
),
|
||||
(
|
||||
&child_info2,
|
||||
@@ -2057,11 +2149,13 @@ mod tests {
|
||||
),
|
||||
]
|
||||
.into_iter(),
|
||||
state_version,
|
||||
);
|
||||
let mut remote_storage = remote_backend.into_storage();
|
||||
remote_storage.consolidate(transaction);
|
||||
let remote_backend = TrieBackend::new(remote_storage, remote_root);
|
||||
let remote_proof = prove_child_read(remote_backend, &child_info1, &[b"key1"]).unwrap();
|
||||
let size = remote_proof.encoded_size();
|
||||
let remote_proof = test_compact(remote_proof, &remote_root);
|
||||
let local_result1 = read_child_proof_check::<BlakeTwo256, _>(
|
||||
remote_root,
|
||||
@@ -2071,11 +2165,13 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(local_result1.len(), 1);
|
||||
assert_eq!(local_result1.get(&b"key1"[..]), Some(&Some(b"val2".to_vec())));
|
||||
assert_eq!(local_result1.get(&b"key1"[..]), Some(&Some(vec![5u8; 32])));
|
||||
size
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_storage_uuid() {
|
||||
let state_version = StateVersion::V0;
|
||||
let child_info_1 = ChildInfo::new_default(b"sub_test1");
|
||||
let child_info_2 = ChildInfo::new_default(b"sub_test2");
|
||||
|
||||
@@ -2083,12 +2179,12 @@ mod tests {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
|
||||
let mut transaction = {
|
||||
let backend = test_trie();
|
||||
let backend = test_trie(state_version);
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let mut ext = Ext::new(&mut overlay, &mut cache, &backend, None);
|
||||
ext.set_child_storage(&child_info_1, b"abc".to_vec(), b"def".to_vec());
|
||||
ext.set_child_storage(&child_info_2, b"abc".to_vec(), b"def".to_vec());
|
||||
ext.storage_root();
|
||||
ext.storage_root(state_version);
|
||||
cache.transaction.unwrap()
|
||||
};
|
||||
let mut duplicate = false;
|
||||
@@ -2108,7 +2204,7 @@ mod tests {
|
||||
b"aaa".to_vec() => b"0".to_vec(),
|
||||
b"bbb".to_vec() => b"".to_vec()
|
||||
];
|
||||
let state = InMemoryBackend::<BlakeTwo256>::from(initial);
|
||||
let state = InMemoryBackend::<BlakeTwo256>::from((initial, StateVersion::default()));
|
||||
let backend = state.as_trie_backend().unwrap();
|
||||
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
@@ -2134,12 +2230,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn runtime_registered_extensions_are_removed_after_execution() {
|
||||
let state_version = StateVersion::default();
|
||||
use sp_externalities::ExternalitiesExt;
|
||||
sp_externalities::decl_extension! {
|
||||
struct DummyExt(u32);
|
||||
}
|
||||
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let backend = trie_backend::tests::test_trie(state_version);
|
||||
let mut overlayed_changes = Default::default();
|
||||
let wasm_code = RuntimeCode::empty();
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ use hash_db::Hasher;
|
||||
pub use offchain::OffchainOverlayedChanges;
|
||||
use sp_core::{
|
||||
offchain::OffchainOverlayedChange,
|
||||
storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo},
|
||||
storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo, StateVersion},
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use sp_externalities::{Extension, Extensions};
|
||||
@@ -502,11 +502,12 @@ impl OverlayedChanges {
|
||||
backend: &B,
|
||||
parent_hash: H::Out,
|
||||
mut cache: StorageTransactionCache<B::Transaction, H>,
|
||||
state_version: StateVersion,
|
||||
) -> Result<StorageChanges<B::Transaction, H>, DefaultError>
|
||||
where
|
||||
H::Out: Ord + Encode + 'static,
|
||||
{
|
||||
self.drain_storage_changes(backend, parent_hash, &mut cache)
|
||||
self.drain_storage_changes(backend, parent_hash, &mut cache, state_version)
|
||||
}
|
||||
|
||||
/// Drain all changes into a [`StorageChanges`] instance. Leave empty overlay in place.
|
||||
@@ -515,13 +516,14 @@ impl OverlayedChanges {
|
||||
backend: &B,
|
||||
_parent_hash: H::Out,
|
||||
mut cache: &mut StorageTransactionCache<B::Transaction, H>,
|
||||
state_version: StateVersion,
|
||||
) -> Result<StorageChanges<B::Transaction, H>, DefaultError>
|
||||
where
|
||||
H::Out: Ord + Encode + 'static,
|
||||
{
|
||||
// If the transaction does not exist, we generate it.
|
||||
if cache.transaction.is_none() {
|
||||
self.storage_root(backend, &mut cache);
|
||||
self.storage_root(backend, &mut cache, state_version);
|
||||
}
|
||||
|
||||
let (transaction, transaction_storage_root) = cache
|
||||
@@ -580,6 +582,7 @@ impl OverlayedChanges {
|
||||
&self,
|
||||
backend: &B,
|
||||
cache: &mut StorageTransactionCache<B::Transaction, H>,
|
||||
state_version: StateVersion,
|
||||
) -> H::Out
|
||||
where
|
||||
H::Out: Ord + Encode,
|
||||
@@ -589,7 +592,7 @@ impl OverlayedChanges {
|
||||
(info, changes.map(|(k, v)| (&k[..], v.value().map(|v| &v[..]))))
|
||||
});
|
||||
|
||||
let (root, transaction) = backend.full_storage_root(delta, child_delta);
|
||||
let (root, transaction) = backend.full_storage_root(delta, child_delta, state_version);
|
||||
|
||||
cache.transaction = Some(transaction);
|
||||
cache.transaction_storage_root = Some(root);
|
||||
@@ -830,6 +833,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn overlayed_storage_root_works() {
|
||||
let state_version = StateVersion::default();
|
||||
let initial: BTreeMap<_, _> = vec![
|
||||
(b"doe".to_vec(), b"reindeer".to_vec()),
|
||||
(b"dog".to_vec(), b"puppyXXX".to_vec()),
|
||||
@@ -838,7 +842,7 @@ mod tests {
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let backend = InMemoryBackend::<Blake2Hasher>::from(initial);
|
||||
let backend = InMemoryBackend::<Blake2Hasher>::from((initial, state_version));
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
|
||||
overlay.start_transaction();
|
||||
@@ -856,7 +860,7 @@ mod tests {
|
||||
const ROOT: [u8; 32] =
|
||||
hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
|
||||
|
||||
assert_eq!(&ext.storage_root()[..], &ROOT);
|
||||
assert_eq!(&ext.storage_root(state_version)[..], &ROOT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -26,14 +26,11 @@ use codec::{Codec, Decode, Encode};
|
||||
use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX};
|
||||
use log::debug;
|
||||
use parking_lot::RwLock;
|
||||
use sp_core::storage::ChildInfo;
|
||||
use sp_core::storage::{ChildInfo, StateVersion};
|
||||
pub use sp_trie::trie_types::TrieError;
|
||||
use sp_trie::{
|
||||
empty_child_trie_root, read_child_trie_value_with, read_trie_value_with, record_all_keys,
|
||||
MemoryDB, StorageProof,
|
||||
};
|
||||
pub use sp_trie::{
|
||||
trie_types::{Layout, TrieError},
|
||||
Recorder,
|
||||
LayoutV1, MemoryDB, Recorder, StorageProof,
|
||||
};
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
@@ -59,7 +56,8 @@ where
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
read_trie_value_with::<Layout<H>, _, Ephemeral<S, H>>(
|
||||
// V1 is equivalent to V0 on read.
|
||||
read_trie_value_with::<LayoutV1<H>, _, Ephemeral<S, H>>(
|
||||
&eph,
|
||||
self.backend.root(),
|
||||
key,
|
||||
@@ -78,14 +76,16 @@ where
|
||||
let root = self
|
||||
.storage(storage_key)?
|
||||
.and_then(|r| Decode::decode(&mut &r[..]).ok())
|
||||
.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>());
|
||||
// V1 is equivalent to V0 on empty trie
|
||||
.unwrap_or_else(|| empty_child_trie_root::<LayoutV1<H>>());
|
||||
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral::new(self.backend.backend_storage(), &mut read_overlay);
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
read_child_trie_value_with::<Layout<H>, _, _>(
|
||||
// V1 is equivalent to V0 on read
|
||||
read_child_trie_value_with::<LayoutV1<H>, _, _>(
|
||||
child_info.keyspace(),
|
||||
&eph,
|
||||
&root.as_ref(),
|
||||
@@ -102,7 +102,8 @@ where
|
||||
|
||||
let mut iter = move || -> Result<(), Box<TrieError<H::Out>>> {
|
||||
let root = self.backend.root();
|
||||
record_all_keys::<Layout<H>, _>(&eph, root, &mut *self.proof_recorder)
|
||||
// V1 and V is equivalent to V0 on read and recorder is key read.
|
||||
record_all_keys::<LayoutV1<H>, _>(&eph, root, &mut *self.proof_recorder)
|
||||
};
|
||||
|
||||
if let Err(e) = iter() {
|
||||
@@ -338,22 +339,24 @@ where
|
||||
fn storage_root<'b>(
|
||||
&self,
|
||||
delta: impl Iterator<Item = (&'b [u8], Option<&'b [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (H::Out, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord,
|
||||
{
|
||||
self.0.storage_root(delta)
|
||||
self.0.storage_root(delta, state_version)
|
||||
}
|
||||
|
||||
fn child_storage_root<'b>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item = (&'b [u8], Option<&'b [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (H::Out, bool, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord,
|
||||
{
|
||||
self.0.child_storage_root(child_info, delta)
|
||||
self.0.child_storage_root(child_info, delta, state_version)
|
||||
}
|
||||
|
||||
fn register_overlay_stats(&self, _stats: &crate::stats::StateMachineStats) {}
|
||||
@@ -401,13 +404,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn proof_is_empty_until_value_is_read() {
|
||||
let trie_backend = test_trie();
|
||||
proof_is_empty_until_value_is_read_inner(StateVersion::V0);
|
||||
proof_is_empty_until_value_is_read_inner(StateVersion::V1);
|
||||
}
|
||||
fn proof_is_empty_until_value_is_read_inner(test_hash: StateVersion) {
|
||||
let trie_backend = test_trie(test_hash);
|
||||
assert!(test_proving(&trie_backend).extract_proof().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proof_is_non_empty_after_value_is_read() {
|
||||
let trie_backend = test_trie();
|
||||
proof_is_non_empty_after_value_is_read_inner(StateVersion::V0);
|
||||
proof_is_non_empty_after_value_is_read_inner(StateVersion::V1);
|
||||
}
|
||||
fn proof_is_non_empty_after_value_is_read_inner(test_hash: StateVersion) {
|
||||
let trie_backend = test_trie(test_hash);
|
||||
let backend = test_proving(&trie_backend);
|
||||
assert_eq!(backend.storage(b"key").unwrap(), Some(b"value".to_vec()));
|
||||
assert!(!backend.extract_proof().is_empty());
|
||||
@@ -425,58 +436,82 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn passes_through_backend_calls() {
|
||||
let trie_backend = test_trie();
|
||||
passes_through_backend_calls_inner(StateVersion::V0);
|
||||
passes_through_backend_calls_inner(StateVersion::V1);
|
||||
}
|
||||
fn passes_through_backend_calls_inner(state_version: StateVersion) {
|
||||
let trie_backend = test_trie(state_version);
|
||||
let proving_backend = test_proving(&trie_backend);
|
||||
assert_eq!(trie_backend.storage(b"key").unwrap(), proving_backend.storage(b"key").unwrap());
|
||||
assert_eq!(trie_backend.pairs(), proving_backend.pairs());
|
||||
|
||||
let (trie_root, mut trie_mdb) = trie_backend.storage_root(std::iter::empty());
|
||||
let (proving_root, mut proving_mdb) = proving_backend.storage_root(std::iter::empty());
|
||||
let (trie_root, mut trie_mdb) =
|
||||
trie_backend.storage_root(std::iter::empty(), state_version);
|
||||
let (proving_root, mut proving_mdb) =
|
||||
proving_backend.storage_root(std::iter::empty(), state_version);
|
||||
assert_eq!(trie_root, proving_root);
|
||||
assert_eq!(trie_mdb.drain(), proving_mdb.drain());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proof_recorded_and_checked() {
|
||||
let contents = (0..64).map(|i| (vec![i], Some(vec![i]))).collect::<Vec<_>>();
|
||||
fn proof_recorded_and_checked_top() {
|
||||
proof_recorded_and_checked_inner(StateVersion::V0);
|
||||
proof_recorded_and_checked_inner(StateVersion::V1);
|
||||
}
|
||||
fn proof_recorded_and_checked_inner(state_version: StateVersion) {
|
||||
let size_content = 34; // above hashable value treshold.
|
||||
let value_range = 0..64;
|
||||
let contents = value_range
|
||||
.clone()
|
||||
.map(|i| (vec![i], Some(vec![i; size_content])))
|
||||
.collect::<Vec<_>>();
|
||||
let in_memory = InMemoryBackend::<BlakeTwo256>::default();
|
||||
let in_memory = in_memory.update(vec![(None, contents)]);
|
||||
let in_memory_root = in_memory.storage_root(::std::iter::empty()).0;
|
||||
(0..64).for_each(|i| assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i]));
|
||||
let in_memory = in_memory.update(vec![(None, contents)], state_version);
|
||||
let in_memory_root = in_memory.storage_root(std::iter::empty(), state_version).0;
|
||||
value_range.clone().for_each(|i| {
|
||||
assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i; size_content])
|
||||
});
|
||||
|
||||
let trie = in_memory.as_trie_backend().unwrap();
|
||||
let trie_root = trie.storage_root(::std::iter::empty()).0;
|
||||
let trie_root = trie.storage_root(std::iter::empty(), state_version).0;
|
||||
assert_eq!(in_memory_root, trie_root);
|
||||
(0..64).for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i]));
|
||||
value_range
|
||||
.clone()
|
||||
.for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i; size_content]));
|
||||
|
||||
let proving = ProvingBackend::new(trie);
|
||||
assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]);
|
||||
assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42; size_content]);
|
||||
|
||||
let proof = proving.extract_proof();
|
||||
|
||||
let proof_check =
|
||||
create_proof_check_backend::<BlakeTwo256>(in_memory_root.into(), proof).unwrap();
|
||||
assert_eq!(proof_check.storage(&[42]).unwrap().unwrap(), vec![42]);
|
||||
assert_eq!(proof_check.storage(&[42]).unwrap().unwrap(), vec![42; size_content]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proof_recorded_and_checked_with_child() {
|
||||
proof_recorded_and_checked_with_child_inner(StateVersion::V0);
|
||||
proof_recorded_and_checked_with_child_inner(StateVersion::V1);
|
||||
}
|
||||
fn proof_recorded_and_checked_with_child_inner(state_version: StateVersion) {
|
||||
let child_info_1 = ChildInfo::new_default(b"sub1");
|
||||
let child_info_2 = ChildInfo::new_default(b"sub2");
|
||||
let child_info_1 = &child_info_1;
|
||||
let child_info_2 = &child_info_2;
|
||||
let contents = vec![
|
||||
(None, (0..64).map(|i| (vec![i], Some(vec![i]))).collect()),
|
||||
(None, (0..64).map(|i| (vec![i], Some(vec![i]))).collect::<Vec<_>>()),
|
||||
(Some(child_info_1.clone()), (28..65).map(|i| (vec![i], Some(vec![i]))).collect()),
|
||||
(Some(child_info_2.clone()), (10..15).map(|i| (vec![i], Some(vec![i]))).collect()),
|
||||
];
|
||||
let in_memory = InMemoryBackend::<BlakeTwo256>::default();
|
||||
let in_memory = in_memory.update(contents);
|
||||
let in_memory = in_memory.update(contents, state_version);
|
||||
let child_storage_keys = vec![child_info_1.to_owned(), child_info_2.to_owned()];
|
||||
let in_memory_root = in_memory
|
||||
.full_storage_root(
|
||||
std::iter::empty(),
|
||||
child_storage_keys.iter().map(|k| (k, std::iter::empty())),
|
||||
state_version,
|
||||
)
|
||||
.0;
|
||||
(0..64).for_each(|i| assert_eq!(in_memory.storage(&[i]).unwrap().unwrap(), vec![i]));
|
||||
@@ -488,7 +523,7 @@ mod tests {
|
||||
});
|
||||
|
||||
let trie = in_memory.as_trie_backend().unwrap();
|
||||
let trie_root = trie.storage_root(std::iter::empty()).0;
|
||||
let trie_root = trie.storage_root(std::iter::empty(), state_version).0;
|
||||
assert_eq!(in_memory_root, trie_root);
|
||||
(0..64).for_each(|i| assert_eq!(trie.storage(&[i]).unwrap().unwrap(), vec![i]));
|
||||
|
||||
@@ -516,7 +551,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn storage_proof_encoded_size_estimation_works() {
|
||||
let trie_backend = test_trie();
|
||||
storage_proof_encoded_size_estimation_works_inner(StateVersion::V0);
|
||||
storage_proof_encoded_size_estimation_works_inner(StateVersion::V1);
|
||||
}
|
||||
fn storage_proof_encoded_size_estimation_works_inner(state_version: StateVersion) {
|
||||
let trie_backend = test_trie(state_version);
|
||||
let backend = test_proving(&trie_backend);
|
||||
|
||||
let check_estimation =
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{Backend, StorageKey, StorageValue};
|
||||
use codec::Encode;
|
||||
use hash_db::Hasher;
|
||||
use sp_core::{
|
||||
storage::{ChildInfo, TrackedStorageKey},
|
||||
storage::{ChildInfo, StateVersion, TrackedStorageKey},
|
||||
traits::Externalities,
|
||||
Blake2Hasher,
|
||||
};
|
||||
@@ -145,11 +145,15 @@ impl<'a, H: Hasher, B: 'a + Backend<H>> Externalities for ReadOnlyExternalities<
|
||||
unimplemented!("storage_append is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn storage_root(&mut self) -> Vec<u8> {
|
||||
fn storage_root(&mut self, _state_version: StateVersion) -> Vec<u8> {
|
||||
unimplemented!("storage_root is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
fn child_storage_root(&mut self, _child_info: &ChildInfo) -> Vec<u8> {
|
||||
fn child_storage_root(
|
||||
&mut self,
|
||||
_child_info: &ChildInfo,
|
||||
_state_version: StateVersion,
|
||||
) -> Vec<u8> {
|
||||
unimplemented!("child_storage_root is not supported in ReadOnlyExternalities")
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ use sp_core::{
|
||||
offchain::testing::TestPersistentOffchainDB,
|
||||
storage::{
|
||||
well_known_keys::{is_child_storage_key, CODE},
|
||||
Storage,
|
||||
StateVersion, Storage,
|
||||
},
|
||||
testing::TaskExecutor,
|
||||
traits::TaskExecutorExt,
|
||||
@@ -54,6 +54,8 @@ where
|
||||
pub backend: InMemoryBackend<H>,
|
||||
/// Extensions.
|
||||
pub extensions: Extensions,
|
||||
/// State version to use during tests.
|
||||
pub state_version: StateVersion,
|
||||
}
|
||||
|
||||
impl<H: Hasher> TestExternalities<H>
|
||||
@@ -72,18 +74,31 @@ where
|
||||
|
||||
/// Create a new instance of `TestExternalities` with storage.
|
||||
pub fn new(storage: Storage) -> Self {
|
||||
Self::new_with_code(&[], storage)
|
||||
Self::new_with_code_and_state(&[], storage, Default::default())
|
||||
}
|
||||
|
||||
/// Create a new instance of `TestExternalities` with storage for a given state version.
|
||||
pub fn new_with_state_version(storage: Storage, state_version: StateVersion) -> Self {
|
||||
Self::new_with_code_and_state(&[], storage, state_version)
|
||||
}
|
||||
|
||||
/// New empty test externalities.
|
||||
pub fn new_empty() -> Self {
|
||||
Self::new_with_code(&[], Storage::default())
|
||||
Self::new_with_code_and_state(&[], Storage::default(), Default::default())
|
||||
}
|
||||
|
||||
/// Create a new instance of `TestExternalities` with code and storage.
|
||||
pub fn new_with_code(code: &[u8], mut storage: Storage) -> Self {
|
||||
let overlay = OverlayedChanges::default();
|
||||
pub fn new_with_code(code: &[u8], storage: Storage) -> Self {
|
||||
Self::new_with_code_and_state(code, storage, Default::default())
|
||||
}
|
||||
|
||||
/// Create a new instance of `TestExternalities` with code and storage for a given state
|
||||
/// version.
|
||||
pub fn new_with_code_and_state(
|
||||
code: &[u8],
|
||||
mut storage: Storage,
|
||||
state_version: StateVersion,
|
||||
) -> Self {
|
||||
assert!(storage.top.keys().all(|key| !is_child_storage_key(key)));
|
||||
assert!(storage.children_default.keys().all(|key| is_child_storage_key(key)));
|
||||
|
||||
@@ -94,12 +109,15 @@ where
|
||||
|
||||
let offchain_db = TestPersistentOffchainDB::new();
|
||||
|
||||
let backend = (storage, state_version).into();
|
||||
|
||||
TestExternalities {
|
||||
overlay,
|
||||
overlay: OverlayedChanges::default(),
|
||||
offchain_db,
|
||||
extensions,
|
||||
backend: storage.into(),
|
||||
backend,
|
||||
storage_transaction_cache: Default::default(),
|
||||
state_version,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,14 +138,14 @@ where
|
||||
|
||||
/// Insert key/value into backend
|
||||
pub fn insert(&mut self, k: StorageKey, v: StorageValue) {
|
||||
self.backend.insert(vec![(None, vec![(k, Some(v))])]);
|
||||
self.backend.insert(vec![(None, vec![(k, Some(v))])], self.state_version);
|
||||
}
|
||||
|
||||
/// Insert key/value into backend.
|
||||
///
|
||||
/// This only supports inserting keys in child tries.
|
||||
pub fn insert_child(&mut self, c: sp_core::storage::ChildInfo, k: StorageKey, v: StorageValue) {
|
||||
self.backend.insert(vec![(Some(c), vec![(k, Some(v))])]);
|
||||
self.backend.insert(vec![(Some(c), vec![(k, Some(v))])], self.state_version);
|
||||
}
|
||||
|
||||
/// Registers the given extension for this instance.
|
||||
@@ -151,7 +169,7 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
self.backend.update(transaction)
|
||||
self.backend.update(transaction, self.state_version)
|
||||
}
|
||||
|
||||
/// Commit all pending changes to the underlying backend.
|
||||
@@ -164,6 +182,7 @@ where
|
||||
&self.backend,
|
||||
Default::default(),
|
||||
&mut Default::default(),
|
||||
self.state_version,
|
||||
)?;
|
||||
|
||||
self.backend
|
||||
@@ -240,7 +259,8 @@ where
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new(Default::default())
|
||||
// default to default version.
|
||||
Self::new_with_state_version(Storage::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +269,16 @@ where
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
{
|
||||
fn from(storage: Storage) -> Self {
|
||||
Self::new(storage)
|
||||
Self::new_with_state_version(storage, Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> From<(Storage, StateVersion)> for TestExternalities<H>
|
||||
where
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
{
|
||||
fn from((storage, state_version): (Storage, StateVersion)) -> Self {
|
||||
Self::new_with_state_version(storage, state_version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,14 +338,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn commit_should_work() {
|
||||
let mut ext = TestExternalities::<BlakeTwo256>::default();
|
||||
let storage = Storage::default(); // avoid adding the trie threshold.
|
||||
let mut ext = TestExternalities::<BlakeTwo256>::from((storage, Default::default()));
|
||||
let mut ext = ext.ext();
|
||||
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
|
||||
ext.set_storage(b"dog".to_vec(), b"puppy".to_vec());
|
||||
ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec());
|
||||
let root =
|
||||
H256::from(hex!("ed4d8c799d996add422395a6abd7545491d40bd838d738afafa1b8a4de625489"));
|
||||
assert_eq!(H256::from_slice(ext.storage_root().as_slice()), root);
|
||||
assert_eq!(H256::from_slice(ext.storage_root(Default::default()).as_slice()), root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -24,12 +24,12 @@ use crate::{
|
||||
};
|
||||
use codec::{Codec, Decode};
|
||||
use hash_db::Hasher;
|
||||
use sp_core::storage::{ChildInfo, ChildType};
|
||||
use sp_core::storage::{ChildInfo, ChildType, StateVersion};
|
||||
use sp_std::{boxed::Box, vec::Vec};
|
||||
use sp_trie::{
|
||||
child_delta_trie_root, delta_trie_root, empty_child_trie_root,
|
||||
trie_types::{Layout, TrieDB, TrieError},
|
||||
Trie,
|
||||
trie_types::{TrieDB, TrieError},
|
||||
LayoutV0, LayoutV1, Trie,
|
||||
};
|
||||
|
||||
/// Patricia trie-based backend. Transaction type is an overlay of changes to commit.
|
||||
@@ -186,6 +186,7 @@ where
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (H::Out, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord,
|
||||
@@ -195,8 +196,14 @@ where
|
||||
|
||||
{
|
||||
let mut eph = Ephemeral::new(self.essence.backend_storage(), &mut write_overlay);
|
||||
let res = match state_version {
|
||||
StateVersion::V0 =>
|
||||
delta_trie_root::<LayoutV0<H>, _, _, _, _, _>(&mut eph, root, delta),
|
||||
StateVersion::V1 =>
|
||||
delta_trie_root::<LayoutV1<H>, _, _, _, _, _>(&mut eph, root, delta),
|
||||
};
|
||||
|
||||
match delta_trie_root::<Layout<H>, _, _, _, _, _>(&mut eph, root, delta) {
|
||||
match res {
|
||||
Ok(ret) => root = ret,
|
||||
Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e),
|
||||
}
|
||||
@@ -209,14 +216,14 @@ where
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
state_version: StateVersion,
|
||||
) -> (H::Out, bool, Self::Transaction)
|
||||
where
|
||||
H::Out: Ord,
|
||||
{
|
||||
let default_root = match child_info.child_type() {
|
||||
ChildType::ParentKeyId => empty_child_trie_root::<Layout<H>>(),
|
||||
ChildType::ParentKeyId => empty_child_trie_root::<LayoutV1<H>>(),
|
||||
};
|
||||
|
||||
let mut write_overlay = S::Overlay::default();
|
||||
let prefixed_storage_key = child_info.prefixed_storage_key();
|
||||
let mut root = match self.storage(prefixed_storage_key.as_slice()) {
|
||||
@@ -231,13 +238,20 @@ where
|
||||
|
||||
{
|
||||
let mut eph = Ephemeral::new(self.essence.backend_storage(), &mut write_overlay);
|
||||
|
||||
match child_delta_trie_root::<Layout<H>, _, _, _, _, _, _>(
|
||||
child_info.keyspace(),
|
||||
&mut eph,
|
||||
root,
|
||||
delta,
|
||||
) {
|
||||
match match state_version {
|
||||
StateVersion::V0 => child_delta_trie_root::<LayoutV0<H>, _, _, _, _, _, _>(
|
||||
child_info.keyspace(),
|
||||
&mut eph,
|
||||
root,
|
||||
delta,
|
||||
),
|
||||
StateVersion::V1 => child_delta_trie_root::<LayoutV1<H>, _, _, _, _, _, _>(
|
||||
child_info.keyspace(),
|
||||
&mut eph,
|
||||
root,
|
||||
delta,
|
||||
),
|
||||
} {
|
||||
Ok(ret) => root = ret,
|
||||
Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e),
|
||||
}
|
||||
@@ -269,52 +283,91 @@ pub mod tests {
|
||||
use codec::Encode;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::traits::BlakeTwo256;
|
||||
use sp_trie::{trie_types::TrieDBMut, KeySpacedDBMut, PrefixedMemoryDB, TrieMut};
|
||||
use sp_trie::{
|
||||
trie_types::{TrieDBMutV0, TrieDBMutV1},
|
||||
KeySpacedDBMut, PrefixedMemoryDB, TrieMut,
|
||||
};
|
||||
use std::{collections::HashSet, iter};
|
||||
|
||||
const CHILD_KEY_1: &[u8] = b"sub1";
|
||||
|
||||
fn test_db() -> (PrefixedMemoryDB<BlakeTwo256>, H256) {
|
||||
pub(crate) fn test_db(state_version: StateVersion) -> (PrefixedMemoryDB<BlakeTwo256>, H256) {
|
||||
let child_info = ChildInfo::new_default(CHILD_KEY_1);
|
||||
let mut root = H256::default();
|
||||
let mut mdb = PrefixedMemoryDB::<BlakeTwo256>::default();
|
||||
{
|
||||
let mut mdb = KeySpacedDBMut::new(&mut mdb, child_info.keyspace());
|
||||
let mut trie = TrieDBMut::new(&mut mdb, &mut root);
|
||||
trie.insert(b"value3", &[142; 33]).expect("insert failed");
|
||||
trie.insert(b"value4", &[124; 33]).expect("insert failed");
|
||||
match state_version {
|
||||
StateVersion::V0 => {
|
||||
let mut trie = TrieDBMutV0::new(&mut mdb, &mut root);
|
||||
trie.insert(b"value3", &[142; 33]).expect("insert failed");
|
||||
trie.insert(b"value4", &[124; 33]).expect("insert failed");
|
||||
},
|
||||
StateVersion::V1 => {
|
||||
let mut trie = TrieDBMutV1::new(&mut mdb, &mut root);
|
||||
trie.insert(b"value3", &[142; 33]).expect("insert failed");
|
||||
trie.insert(b"value4", &[124; 33]).expect("insert failed");
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
{
|
||||
let mut sub_root = Vec::new();
|
||||
root.encode_to(&mut sub_root);
|
||||
let mut trie = TrieDBMut::new(&mut mdb, &mut root);
|
||||
trie.insert(child_info.prefixed_storage_key().as_slice(), &sub_root[..])
|
||||
.expect("insert failed");
|
||||
trie.insert(b"key", b"value").expect("insert failed");
|
||||
trie.insert(b"value1", &[42]).expect("insert failed");
|
||||
trie.insert(b"value2", &[24]).expect("insert failed");
|
||||
trie.insert(b":code", b"return 42").expect("insert failed");
|
||||
for i in 128u8..255u8 {
|
||||
trie.insert(&[i], &[i]).unwrap();
|
||||
|
||||
fn build<L: sp_trie::TrieLayout>(
|
||||
mut trie: sp_trie::TrieDBMut<L>,
|
||||
child_info: &ChildInfo,
|
||||
sub_root: &[u8],
|
||||
) {
|
||||
trie.insert(child_info.prefixed_storage_key().as_slice(), sub_root)
|
||||
.expect("insert failed");
|
||||
trie.insert(b"key", b"value").expect("insert failed");
|
||||
trie.insert(b"value1", &[42]).expect("insert failed");
|
||||
trie.insert(b"value2", &[24]).expect("insert failed");
|
||||
trie.insert(b":code", b"return 42").expect("insert failed");
|
||||
for i in 128u8..255u8 {
|
||||
trie.insert(&[i], &[i]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
match state_version {
|
||||
StateVersion::V0 => {
|
||||
let trie = TrieDBMutV0::new(&mut mdb, &mut root);
|
||||
build(trie, &child_info, &sub_root[..])
|
||||
},
|
||||
StateVersion::V1 => {
|
||||
let trie = TrieDBMutV1::new(&mut mdb, &mut root);
|
||||
build(trie, &child_info, &sub_root[..])
|
||||
},
|
||||
};
|
||||
}
|
||||
(mdb, root)
|
||||
}
|
||||
|
||||
pub(crate) fn test_trie() -> TrieBackend<PrefixedMemoryDB<BlakeTwo256>, BlakeTwo256> {
|
||||
let (mdb, root) = test_db();
|
||||
pub(crate) fn test_trie(
|
||||
hashed_value: StateVersion,
|
||||
) -> TrieBackend<PrefixedMemoryDB<BlakeTwo256>, BlakeTwo256> {
|
||||
let (mdb, root) = test_db(hashed_value);
|
||||
TrieBackend::new(mdb, root)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_from_storage_returns_some() {
|
||||
assert_eq!(test_trie().storage(b"key").unwrap(), Some(b"value".to_vec()));
|
||||
read_from_storage_returns_some_inner(StateVersion::V0);
|
||||
read_from_storage_returns_some_inner(StateVersion::V1);
|
||||
}
|
||||
fn read_from_storage_returns_some_inner(state_version: StateVersion) {
|
||||
assert_eq!(test_trie(state_version).storage(b"key").unwrap(), Some(b"value".to_vec()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_from_child_storage_returns_some() {
|
||||
let test_trie = test_trie();
|
||||
read_from_child_storage_returns_some_inner(StateVersion::V0);
|
||||
read_from_child_storage_returns_some_inner(StateVersion::V1);
|
||||
}
|
||||
fn read_from_child_storage_returns_some_inner(state_version: StateVersion) {
|
||||
let test_trie = test_trie(state_version);
|
||||
assert_eq!(
|
||||
test_trie
|
||||
.child_storage(&ChildInfo::new_default(CHILD_KEY_1), b"value3")
|
||||
@@ -341,12 +394,20 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn read_from_storage_returns_none() {
|
||||
assert_eq!(test_trie().storage(b"non-existing-key").unwrap(), None);
|
||||
read_from_storage_returns_none_inner(StateVersion::V0);
|
||||
read_from_storage_returns_none_inner(StateVersion::V1);
|
||||
}
|
||||
fn read_from_storage_returns_none_inner(state_version: StateVersion) {
|
||||
assert_eq!(test_trie(state_version).storage(b"non-existing-key").unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pairs_are_not_empty_on_non_empty_storage() {
|
||||
assert!(!test_trie().pairs().is_empty());
|
||||
pairs_are_not_empty_on_non_empty_storage_inner(StateVersion::V0);
|
||||
pairs_are_not_empty_on_non_empty_storage_inner(StateVersion::V1);
|
||||
}
|
||||
fn pairs_are_not_empty_on_non_empty_storage_inner(state_version: StateVersion) {
|
||||
assert!(!test_trie(state_version).pairs().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -361,25 +422,35 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn storage_root_is_non_default() {
|
||||
assert!(test_trie().storage_root(iter::empty()).0 != H256::repeat_byte(0));
|
||||
storage_root_is_non_default_inner(StateVersion::V0);
|
||||
storage_root_is_non_default_inner(StateVersion::V1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_root_transaction_is_empty() {
|
||||
assert!(test_trie().storage_root(iter::empty()).1.drain().is_empty());
|
||||
fn storage_root_is_non_default_inner(state_version: StateVersion) {
|
||||
assert!(
|
||||
test_trie(state_version).storage_root(iter::empty(), state_version).0 !=
|
||||
H256::repeat_byte(0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_root_transaction_is_non_empty() {
|
||||
let (new_root, mut tx) =
|
||||
test_trie().storage_root(iter::once((&b"new-key"[..], Some(&b"new-value"[..]))));
|
||||
storage_root_transaction_is_non_empty_inner(StateVersion::V0);
|
||||
storage_root_transaction_is_non_empty_inner(StateVersion::V1);
|
||||
}
|
||||
fn storage_root_transaction_is_non_empty_inner(state_version: StateVersion) {
|
||||
let (new_root, mut tx) = test_trie(state_version)
|
||||
.storage_root(iter::once((&b"new-key"[..], Some(&b"new-value"[..]))), state_version);
|
||||
assert!(!tx.drain().is_empty());
|
||||
assert!(new_root != test_trie().storage_root(iter::empty()).0);
|
||||
assert!(new_root != test_trie(state_version).storage_root(iter::empty(), state_version).0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefix_walking_works() {
|
||||
let trie = test_trie();
|
||||
prefix_walking_works_inner(StateVersion::V0);
|
||||
prefix_walking_works_inner(StateVersion::V1);
|
||||
}
|
||||
fn prefix_walking_works_inner(state_version: StateVersion) {
|
||||
let trie = test_trie(state_version);
|
||||
|
||||
let mut seen = HashSet::new();
|
||||
trie.for_keys_with_prefix(b"value", |key| {
|
||||
|
||||
@@ -20,20 +20,23 @@
|
||||
|
||||
use crate::{backend::Consolidate, debug, warn, StorageKey, StorageValue};
|
||||
use codec::Encode;
|
||||
use hash_db::{self, Hasher, Prefix};
|
||||
use hash_db::{self, AsHashDB, HashDB, HashDBRef, Hasher, Prefix};
|
||||
#[cfg(feature = "std")]
|
||||
use parking_lot::RwLock;
|
||||
use sp_core::storage::ChildInfo;
|
||||
use sp_std::{boxed::Box, vec::Vec};
|
||||
use sp_trie::{
|
||||
empty_child_trie_root, read_child_trie_value, read_trie_value,
|
||||
trie_types::{Layout, TrieDB, TrieError},
|
||||
DBValue, KeySpacedDB, MemoryDB, PrefixedMemoryDB, Trie, TrieDBIterator,
|
||||
trie_types::{TrieDB, TrieError},
|
||||
DBValue, KeySpacedDB, PrefixedMemoryDB, Trie, TrieDBIterator, TrieDBKeyIterator,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::Arc;
|
||||
// In this module, we only use layout for read operation and empty root,
|
||||
// where V1 and V0 are equivalent.
|
||||
use sp_trie::LayoutV1 as Layout;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
macro_rules! format {
|
||||
@@ -178,7 +181,7 @@ where
|
||||
child_info: Option<&ChildInfo>,
|
||||
key: &[u8],
|
||||
) -> Result<Option<StorageKey>> {
|
||||
let dyn_eph: &dyn hash_db::HashDBRef<_, _>;
|
||||
let dyn_eph: &dyn HashDBRef<_, _>;
|
||||
let keyspace_eph;
|
||||
if let Some(child_info) = child_info.as_ref() {
|
||||
keyspace_eph = KeySpacedDB::new(self, child_info.keyspace());
|
||||
@@ -189,7 +192,7 @@ where
|
||||
|
||||
let trie =
|
||||
TrieDB::<H>::new(dyn_eph, root).map_err(|e| format!("TrieDB creation error: {}", e))?;
|
||||
let mut iter = trie.iter().map_err(|e| format!("TrieDB iteration error: {}", e))?;
|
||||
let mut iter = trie.key_iter().map_err(|e| format!("TrieDB iteration error: {}", e))?;
|
||||
|
||||
// The key just after the one given in input, basically `key++0`.
|
||||
// Note: We are sure this is the next key if:
|
||||
@@ -205,7 +208,7 @@ where
|
||||
let next_element = iter.next();
|
||||
|
||||
let next_key = if let Some(next_element) = next_element {
|
||||
let (next_key, _) =
|
||||
let next_key =
|
||||
next_element.map_err(|e| format!("TrieDB iterator next error: {}", e))?;
|
||||
Some(next_key)
|
||||
} else {
|
||||
@@ -291,17 +294,7 @@ where
|
||||
&self.root
|
||||
};
|
||||
|
||||
let _ = self.trie_iter_inner(
|
||||
root,
|
||||
prefix,
|
||||
|k, _v| {
|
||||
f(&k);
|
||||
true
|
||||
},
|
||||
child_info,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
self.trie_iter_key_inner(root, prefix, |k| f(k), child_info)
|
||||
}
|
||||
|
||||
/// Execute given closure for all keys starting with prefix.
|
||||
@@ -320,32 +313,70 @@ where
|
||||
};
|
||||
let mut root = H::Out::default();
|
||||
root.as_mut().copy_from_slice(&root_vec);
|
||||
let _ = self.trie_iter_inner(
|
||||
self.trie_iter_key_inner(
|
||||
&root,
|
||||
Some(prefix),
|
||||
|k, _v| {
|
||||
f(&k);
|
||||
|k| {
|
||||
f(k);
|
||||
true
|
||||
},
|
||||
Some(child_info),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
/// Execute given closure for all keys starting with prefix.
|
||||
pub fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], mut f: F) {
|
||||
let _ = self.trie_iter_inner(
|
||||
self.trie_iter_key_inner(
|
||||
&self.root,
|
||||
Some(prefix),
|
||||
|k, _v| {
|
||||
f(&k);
|
||||
|k| {
|
||||
f(k);
|
||||
true
|
||||
},
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
fn trie_iter_key_inner<F: FnMut(&[u8]) -> bool>(
|
||||
&self,
|
||||
root: &H::Out,
|
||||
prefix: Option<&[u8]>,
|
||||
mut f: F,
|
||||
child_info: Option<&ChildInfo>,
|
||||
) {
|
||||
let mut iter = move |db| -> sp_std::result::Result<(), Box<TrieError<H::Out>>> {
|
||||
let trie = TrieDB::<H>::new(db, root)?;
|
||||
let iter = if let Some(prefix) = prefix.as_ref() {
|
||||
TrieDBKeyIterator::new_prefixed(&trie, prefix)?
|
||||
} else {
|
||||
TrieDBKeyIterator::new(&trie)?
|
||||
};
|
||||
|
||||
for x in iter {
|
||||
let key = x?;
|
||||
|
||||
debug_assert!(prefix
|
||||
.as_ref()
|
||||
.map(|prefix| key.starts_with(prefix))
|
||||
.unwrap_or(true));
|
||||
|
||||
if !f(&key) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let result = if let Some(child_info) = child_info {
|
||||
let db = KeySpacedDB::new(self, child_info.keyspace());
|
||||
iter(&db)
|
||||
} else {
|
||||
iter(self)
|
||||
};
|
||||
if let Err(e) = result {
|
||||
debug!(target: "trie", "Error while iterating by prefix: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn trie_iter_inner<F: FnMut(Vec<u8>, Vec<u8>) -> bool>(
|
||||
@@ -414,13 +445,13 @@ pub(crate) struct Ephemeral<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> {
|
||||
overlay: &'a mut S::Overlay,
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> hash_db::AsHashDB<H, DBValue>
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> AsHashDB<H, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn hash_db::HashDB<H, DBValue> + 'b) {
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn HashDB<H, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, DBValue> + 'b) {
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn HashDB<H, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -435,7 +466,7 @@ impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
|
||||
if let Some(val) = hash_db::HashDB::get(self.overlay, key, prefix) {
|
||||
if let Some(val) = HashDB::get(self.overlay, key, prefix) {
|
||||
Some(val)
|
||||
} else {
|
||||
match self.storage.get(&key, prefix) {
|
||||
@@ -449,38 +480,37 @@ impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue>
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
|
||||
hash_db::HashDB::get(self, key, prefix).is_some()
|
||||
HashDB::get(self, key, prefix).is_some()
|
||||
}
|
||||
|
||||
fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out {
|
||||
hash_db::HashDB::insert(self.overlay, prefix, value)
|
||||
HashDB::insert(self.overlay, prefix, value)
|
||||
}
|
||||
|
||||
fn emplace(&mut self, key: H::Out, prefix: Prefix, value: DBValue) {
|
||||
hash_db::HashDB::emplace(self.overlay, key, prefix, value)
|
||||
HashDB::emplace(self.overlay, key, prefix, value)
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: &H::Out, prefix: Prefix) {
|
||||
hash_db::HashDB::remove(self.overlay, key, prefix)
|
||||
HashDB::remove(self.overlay, key, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> hash_db::HashDBRef<H, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> HashDBRef<H, DBValue> for Ephemeral<'a, S, H> {
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
|
||||
hash_db::HashDB::get(self, key, prefix)
|
||||
HashDB::get(self, key, prefix)
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
|
||||
hash_db::HashDB::contains(self, key, prefix)
|
||||
HashDB::contains(self, key, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
/// Key-value pairs storage that is used by trie backend essence.
|
||||
pub trait TrieBackendStorage<H: Hasher>: Send + Sync {
|
||||
/// Type of in-memory overlay.
|
||||
type Overlay: hash_db::HashDB<H, DBValue> + Default + Consolidate;
|
||||
type Overlay: HashDB<H, DBValue> + Default + Consolidate;
|
||||
|
||||
/// Get the value stored at key.
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>>;
|
||||
}
|
||||
@@ -495,35 +525,28 @@ impl<H: Hasher> TrieBackendStorage<H> for Arc<dyn Storage<H>> {
|
||||
}
|
||||
}
|
||||
|
||||
// This implementation is used by test storage trie clients.
|
||||
impl<H: Hasher> TrieBackendStorage<H> for PrefixedMemoryDB<H> {
|
||||
type Overlay = PrefixedMemoryDB<H>;
|
||||
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
|
||||
Ok(hash_db::HashDB::get(self, key, prefix))
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> TrieBackendStorage<H> for MemoryDB<H> {
|
||||
type Overlay = MemoryDB<H>;
|
||||
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
|
||||
Ok(hash_db::HashDB::get(self, key, prefix))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::AsHashDB<H, DBValue>
|
||||
for TrieBackendEssence<S, H>
|
||||
impl<H, KF> TrieBackendStorage<H> for sp_trie::GenericMemoryDB<H, KF>
|
||||
where
|
||||
H: Hasher,
|
||||
KF: sp_trie::KeyFunction<H> + Send + Sync,
|
||||
{
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn hash_db::HashDB<H, DBValue> + 'b) {
|
||||
type Overlay = Self;
|
||||
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
|
||||
Ok(hash_db::HashDB::get(self, key, prefix))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> AsHashDB<H, DBValue> for TrieBackendEssence<S, H> {
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn HashDB<H, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, DBValue> + 'b) {
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn HashDB<H, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue> for TrieBackendEssence<S, H> {
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> HashDB<H, DBValue> for TrieBackendEssence<S, H> {
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
|
||||
if *key == self.empty {
|
||||
return Some([0u8].to_vec())
|
||||
@@ -538,7 +561,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue> for TrieBa
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
|
||||
hash_db::HashDB::get(self, key, prefix).is_some()
|
||||
HashDB::get(self, key, prefix).is_some()
|
||||
}
|
||||
|
||||
fn insert(&mut self, _prefix: Prefix, _value: &[u8]) -> H::Out {
|
||||
@@ -554,15 +577,13 @@ impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue> for TrieBa
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDBRef<H, DBValue>
|
||||
for TrieBackendEssence<S, H>
|
||||
{
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> HashDBRef<H, DBValue> for TrieBackendEssence<S, H> {
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
|
||||
hash_db::HashDB::get(self, key, prefix)
|
||||
HashDB::get(self, key, prefix)
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
|
||||
hash_db::HashDB::contains(self, key, prefix)
|
||||
HashDB::contains(self, key, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,7 +591,9 @@ impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDBRef<H, DBValue>
|
||||
mod test {
|
||||
use super::*;
|
||||
use sp_core::{Blake2Hasher, H256};
|
||||
use sp_trie::{trie_types::TrieDBMut, KeySpacedDBMut, PrefixedMemoryDB, TrieMut};
|
||||
use sp_trie::{
|
||||
trie_types::TrieDBMutV1 as TrieDBMut, KeySpacedDBMut, PrefixedMemoryDB, TrieMut,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn next_storage_key_and_next_child_storage_key_work() {
|
||||
|
||||
@@ -237,6 +237,9 @@ pub mod well_known_keys {
|
||||
}
|
||||
}
|
||||
|
||||
/// Threshold size to start using trie value nodes in state.
|
||||
pub const TRIE_VALUE_NODE_THRESHOLD: u32 = 33;
|
||||
|
||||
/// Information related to a child state.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "std", derive(PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode))]
|
||||
@@ -401,6 +404,54 @@ impl ChildTrieParentKeyId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Different possible state version.
|
||||
///
|
||||
/// V0 and V1 uses a same trie implementation, but V1 will write external value node in the trie for
|
||||
/// value with size at least `TRIE_VALUE_NODE_THRESHOLD`.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum StateVersion {
|
||||
/// Old state version, no value nodes.
|
||||
V0 = 0,
|
||||
/// New state version can use value nodes.
|
||||
V1 = 1,
|
||||
}
|
||||
|
||||
impl Default for StateVersion {
|
||||
fn default() -> Self {
|
||||
StateVersion::V1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StateVersion> for u8 {
|
||||
fn from(version: StateVersion) -> u8 {
|
||||
version as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_std::convert::TryFrom<u8> for StateVersion {
|
||||
type Error = ();
|
||||
fn try_from(val: u8) -> sp_std::result::Result<StateVersion, ()> {
|
||||
match val {
|
||||
0 => Ok(StateVersion::V0),
|
||||
1 => Ok(StateVersion::V1),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StateVersion {
|
||||
/// If defined, values in state of size bigger or equal
|
||||
/// to this threshold will use a separate trie node.
|
||||
/// Otherwhise, value will be inlined in branch or leaf
|
||||
/// node.
|
||||
pub fn state_value_threshold(&self) -> Option<u32> {
|
||||
match self {
|
||||
StateVersion::V0 => None,
|
||||
StateVersion::V1 => Some(TRIE_VALUE_NODE_THRESHOLD),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//! Async externalities.
|
||||
|
||||
use sp_core::{
|
||||
storage::{ChildInfo, TrackedStorageKey},
|
||||
storage::{ChildInfo, StateVersion, TrackedStorageKey},
|
||||
traits::{Externalities, RuntimeSpawn, RuntimeSpawnExt, SpawnNamed, TaskExecutorExt},
|
||||
};
|
||||
use sp_externalities::{Extensions, ExternalitiesExt as _};
|
||||
@@ -126,11 +126,15 @@ impl Externalities for AsyncExternalities {
|
||||
panic!("`storage_append`: should not be used in async externalities!")
|
||||
}
|
||||
|
||||
fn storage_root(&mut self) -> Vec<u8> {
|
||||
fn storage_root(&mut self, _state_version: StateVersion) -> Vec<u8> {
|
||||
panic!("`storage_root`: should not be used in async externalities!")
|
||||
}
|
||||
|
||||
fn child_storage_root(&mut self, _child_info: &ChildInfo) -> Vec<u8> {
|
||||
fn child_storage_root(
|
||||
&mut self,
|
||||
_child_info: &ChildInfo,
|
||||
_state_version: StateVersion,
|
||||
) -> Vec<u8> {
|
||||
panic!("`child_storage_root`: should not be used in async externalities!")
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ pub mod registration {
|
||||
use sp_trie::TrieMut;
|
||||
|
||||
type Hasher = sp_core::Blake2Hasher;
|
||||
type TrieLayout = sp_trie::Layout<Hasher>;
|
||||
type TrieLayout = sp_trie::LayoutV1<Hasher>;
|
||||
|
||||
/// Create a new inherent data provider instance for a given parent block hash.
|
||||
pub fn new_data_provider<B, C>(
|
||||
|
||||
@@ -22,13 +22,13 @@ codec = { package = "parity-scale-codec", version = "2.0.0", default-features =
|
||||
scale-info = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
sp-std = { version = "4.0.0", default-features = false, path = "../std" }
|
||||
hash-db = { version = "0.15.2", default-features = false }
|
||||
trie-db = { version = "0.22.6", default-features = false }
|
||||
trie-root = { version = "0.16.0", default-features = false }
|
||||
memory-db = { version = "0.27.0", default-features = false }
|
||||
trie-db = { version = "0.23.0", default-features = false }
|
||||
trie-root = { version = "0.17.0", default-features = false }
|
||||
memory-db = { version = "0.28.0", default-features = false }
|
||||
sp-core = { version = "4.1.0-dev", default-features = false, path = "../core" }
|
||||
|
||||
[dev-dependencies]
|
||||
trie-bench = "0.28.0"
|
||||
trie-bench = "0.29.0"
|
||||
trie-standardmap = "0.15.2"
|
||||
criterion = "0.3.3"
|
||||
hex-literal = "0.3.4"
|
||||
|
||||
@@ -21,11 +21,11 @@ criterion_main!(benches);
|
||||
|
||||
fn benchmark(c: &mut Criterion) {
|
||||
trie_bench::standard_benchmark::<
|
||||
sp_trie::Layout<sp_runtime::traits::BlakeTwo256>,
|
||||
sp_trie::LayoutV1<sp_runtime::traits::BlakeTwo256>,
|
||||
sp_trie::TrieStream,
|
||||
>(c, "substrate-blake2");
|
||||
trie_bench::standard_benchmark::<
|
||||
sp_trie::Layout<sp_runtime::traits::BlakeTwo256>,
|
||||
sp_trie::LayoutV1<sp_runtime::traits::BlakeTwo256>,
|
||||
sp_trie::TrieStream,
|
||||
>(c, "substrate-keccak");
|
||||
}
|
||||
|
||||
@@ -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..]);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ sp-std = { version = "4.0.0", default-features = false, path = "../std" }
|
||||
sp-runtime = { version = "4.0.0", default-features = false, path = "../runtime" }
|
||||
sp-version-proc-macro = { version = "4.0.0-dev", default-features = false, path = "proc-macro" }
|
||||
parity-wasm = { version = "0.42.2", optional = true }
|
||||
sp-core-hashing-proc-macro = { version = "4.0.0-dev", path = "../core/hashing/proc-macro" }
|
||||
thiserror = { version = "1.0.30", optional = true }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -63,6 +63,7 @@ struct RuntimeVersion {
|
||||
impl_version: u32,
|
||||
apis: u8,
|
||||
transaction_version: u32,
|
||||
state_version: u8,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
@@ -73,6 +74,7 @@ struct ParseRuntimeVersion {
|
||||
spec_version: Option<u32>,
|
||||
impl_version: Option<u32>,
|
||||
transaction_version: Option<u32>,
|
||||
state_version: Option<u8>,
|
||||
}
|
||||
|
||||
impl ParseRuntimeVersion {
|
||||
@@ -122,6 +124,8 @@ impl ParseRuntimeVersion {
|
||||
parse_once(&mut self.impl_version, field_value, Self::parse_num_literal)?;
|
||||
} else if field_name == "transaction_version" {
|
||||
parse_once(&mut self.transaction_version, field_value, Self::parse_num_literal)?;
|
||||
} else if field_name == "state_version" {
|
||||
parse_once(&mut self.state_version, field_value, Self::parse_num_literal_u8)?;
|
||||
} else if field_name == "apis" {
|
||||
// Intentionally ignored
|
||||
//
|
||||
@@ -147,6 +151,18 @@ impl ParseRuntimeVersion {
|
||||
lit.base10_parse::<u32>()
|
||||
}
|
||||
|
||||
fn parse_num_literal_u8(expr: &Expr) -> Result<u8> {
|
||||
let lit = match *expr {
|
||||
Expr::Lit(ExprLit { lit: Lit::Int(ref lit), .. }) => lit,
|
||||
_ =>
|
||||
return Err(Error::new(
|
||||
expr.span(),
|
||||
"only numeric literals (e.g. `10`) are supported here",
|
||||
)),
|
||||
};
|
||||
lit.base10_parse::<u8>()
|
||||
}
|
||||
|
||||
fn parse_str_literal(expr: &Expr) -> Result<String> {
|
||||
let mac = match *expr {
|
||||
Expr::Macro(syn::ExprMacro { ref mac, .. }) => mac,
|
||||
@@ -182,6 +198,7 @@ impl ParseRuntimeVersion {
|
||||
spec_version,
|
||||
impl_version,
|
||||
transaction_version,
|
||||
state_version,
|
||||
} = self;
|
||||
|
||||
Ok(RuntimeVersion {
|
||||
@@ -191,6 +208,7 @@ impl ParseRuntimeVersion {
|
||||
spec_version: required!(spec_version),
|
||||
impl_version: required!(impl_version),
|
||||
transaction_version: required!(transaction_version),
|
||||
state_version: required!(state_version),
|
||||
apis: 0,
|
||||
})
|
||||
}
|
||||
@@ -210,7 +228,6 @@ fn generate_emit_link_section_decl(contents: &[u8], section_name: &str) -> Token
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::DecodeAll;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[test]
|
||||
@@ -223,11 +240,13 @@ mod tests {
|
||||
impl_version: 1,
|
||||
apis: 0,
|
||||
transaction_version: 2,
|
||||
state_version: 1,
|
||||
}
|
||||
.encode();
|
||||
|
||||
assert_eq!(
|
||||
sp_version::RuntimeVersion::decode_all(&mut &version_bytes[..]).unwrap(),
|
||||
sp_version::RuntimeVersion::decode_with_version_hint(&mut &version_bytes[..], Some(4))
|
||||
.unwrap(),
|
||||
sp_version::RuntimeVersion {
|
||||
spec_name: "hello".into(),
|
||||
impl_name: "world".into(),
|
||||
@@ -236,6 +255,7 @@ mod tests {
|
||||
impl_version: 1,
|
||||
apis: Cow::Owned(vec![]),
|
||||
transaction_version: 2,
|
||||
state_version: 1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ use std::collections::HashSet;
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use codec::{Decode, Encode, Input};
|
||||
use scale_info::TypeInfo;
|
||||
pub use sp_runtime::create_runtime_str;
|
||||
use sp_runtime::RuntimeString;
|
||||
pub use sp_runtime::{create_runtime_str, StateVersion};
|
||||
#[doc(hidden)]
|
||||
pub use sp_std;
|
||||
|
||||
@@ -79,6 +79,7 @@ pub mod embed;
|
||||
/// impl_version: 1,
|
||||
/// apis: RUNTIME_API_VERSIONS,
|
||||
/// transaction_version: 2,
|
||||
/// state_version: 1,
|
||||
/// };
|
||||
///
|
||||
/// # const RUNTIME_API_VERSIONS: sp_version::ApisVec = sp_version::create_apis_vec!([]);
|
||||
@@ -154,7 +155,7 @@ macro_rules! create_apis_vec {
|
||||
/// In particular: bug fixes should result in an increment of `spec_version` and possibly
|
||||
/// `authoring_version`, absolutely not `impl_version` since they change the semantics of the
|
||||
/// runtime.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, Default, sp_runtime::RuntimeDebug, TypeInfo)]
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Default, sp_runtime::RuntimeDebug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
pub struct RuntimeVersion {
|
||||
@@ -207,6 +208,53 @@ pub struct RuntimeVersion {
|
||||
///
|
||||
/// It need *not* change when a new module is added or when a dispatchable is added.
|
||||
pub transaction_version: u32,
|
||||
|
||||
/// Version of the state implementation used by this runtime.
|
||||
/// Use of an incorrect version is consensus breaking.
|
||||
pub state_version: u8,
|
||||
}
|
||||
|
||||
impl RuntimeVersion {
|
||||
/// `Decode` while giving a "version hint"
|
||||
///
|
||||
/// There exists multiple versions of [`RuntimeVersion`] and they are versioned using the `Core`
|
||||
/// runtime api:
|
||||
/// - `Core` version < 3 is a runtime version without a transaction version and state version.
|
||||
/// - `Core` version 3 is a runtime version without a state version.
|
||||
/// - `Core` version 4 is the latest runtime version.
|
||||
pub fn decode_with_version_hint<I: Input>(
|
||||
input: &mut I,
|
||||
core_version: Option<u32>,
|
||||
) -> Result<RuntimeVersion, codec::Error> {
|
||||
let spec_name = Decode::decode(input)?;
|
||||
let impl_name = Decode::decode(input)?;
|
||||
let authoring_version = Decode::decode(input)?;
|
||||
let spec_version = Decode::decode(input)?;
|
||||
let impl_version = Decode::decode(input)?;
|
||||
let apis = Decode::decode(input)?;
|
||||
let core_version =
|
||||
if core_version.is_some() { core_version } else { core_version_from_apis(&apis) };
|
||||
let transaction_version =
|
||||
if core_version.map(|v| v >= 3).unwrap_or(false) { Decode::decode(input)? } else { 1 };
|
||||
let state_version =
|
||||
if core_version.map(|v| v >= 4).unwrap_or(false) { Decode::decode(input)? } else { 0 };
|
||||
Ok(RuntimeVersion {
|
||||
spec_name,
|
||||
impl_name,
|
||||
authoring_version,
|
||||
spec_version,
|
||||
impl_version,
|
||||
apis,
|
||||
transaction_version,
|
||||
state_version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for RuntimeVersion {
|
||||
fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
Self::decode_with_version_hint(input, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -225,6 +273,16 @@ impl fmt::Display for RuntimeVersion {
|
||||
}
|
||||
}
|
||||
|
||||
fn has_api_with<P: Fn(u32) -> bool>(apis: &ApisVec, id: &ApiId, predicate: P) -> bool {
|
||||
apis.iter().any(|(s, v)| s == id && predicate(*v))
|
||||
}
|
||||
|
||||
/// Returns the version of the `Core` runtime api.
|
||||
pub fn core_version_from_apis(apis: &ApisVec) -> Option<u32> {
|
||||
let id = sp_core_hashing_proc_macro::blake2b_64!(b"Core");
|
||||
apis.iter().find(|(s, _v)| s == &id).map(|(_s, v)| *v)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl RuntimeVersion {
|
||||
/// Check if this version matches other version for calling into runtime.
|
||||
@@ -237,7 +295,7 @@ impl RuntimeVersion {
|
||||
/// Check if the given api with `api_id` is implemented and the version passes the given
|
||||
/// `predicate`.
|
||||
pub fn has_api_with<P: Fn(u32) -> bool>(&self, id: &ApiId, predicate: P) -> bool {
|
||||
self.apis.iter().any(|(s, v)| s == id && predicate(*v))
|
||||
has_api_with(&self.apis, id, predicate)
|
||||
}
|
||||
|
||||
/// Returns the api version found for api with `id`.
|
||||
@@ -246,6 +304,18 @@ impl RuntimeVersion {
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeVersion {
|
||||
/// Returns state version to use for update.
|
||||
///
|
||||
/// For runtime with core api version less than 4,
|
||||
/// V0 trie version will be applied to state.
|
||||
/// Otherwhise, V1 trie version will be use.
|
||||
pub fn state_version(&self) -> StateVersion {
|
||||
// If version > than 1, keep using latest version.
|
||||
self.state_version.try_into().unwrap_or(StateVersion::V1)
|
||||
}
|
||||
}
|
||||
|
||||
/// The version of the native runtime.
|
||||
///
|
||||
/// In contrast to the bare [`RuntimeVersion`] this also carries a list of `spec_version`s of
|
||||
|
||||
@@ -69,7 +69,7 @@ pub trait ClientBlockImportExt<Block: BlockT>: Sized {
|
||||
impl<B, E, RA, Block> ClientExt<Block> for Client<B, E, Block, RA>
|
||||
where
|
||||
B: sc_client_api::backend::Backend<Block>,
|
||||
E: sc_client_api::CallExecutor<Block> + 'static,
|
||||
E: sc_client_api::CallExecutor<Block> + sc_executor::RuntimeVersionOf + 'static,
|
||||
Self: BlockImport<Block, Error = ConsensusError>,
|
||||
Block: BlockT,
|
||||
{
|
||||
|
||||
@@ -208,13 +208,13 @@ impl<Block: BlockT, ExecutorDispatch, Backend, G: GenesisInit>
|
||||
sc_consensus::LongestChain<Backend, Block>,
|
||||
)
|
||||
where
|
||||
ExecutorDispatch: sc_client_api::CallExecutor<Block> + 'static,
|
||||
ExecutorDispatch:
|
||||
sc_client_api::CallExecutor<Block> + sc_executor::RuntimeVersionOf + 'static,
|
||||
Backend: sc_client_api::backend::Backend<Block>,
|
||||
<Backend as sc_client_api::backend::Backend<Block>>::OffchainStorage: 'static,
|
||||
{
|
||||
let storage = {
|
||||
let mut storage = self.genesis_init.genesis_storage();
|
||||
|
||||
// Add some child storage keys.
|
||||
for (key, child_content) in self.child_storage_extension {
|
||||
storage.children_default.insert(
|
||||
|
||||
@@ -39,7 +39,7 @@ pallet-timestamp = { version = "4.0.0-dev", default-features = false, path = "..
|
||||
sp-finality-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../primitives/finality-grandpa" }
|
||||
sp-trie = { version = "4.0.0", default-features = false, path = "../../primitives/trie" }
|
||||
sp-transaction-pool = { version = "4.0.0-dev", default-features = false, path = "../../primitives/transaction-pool" }
|
||||
trie-db = { version = "0.22.6", default-features = false }
|
||||
trie-db = { version = "0.23.0", default-features = false }
|
||||
parity-util-mem = { version = "0.10.2", default-features = false, features = ["primitive-types"] }
|
||||
sc-service = { version = "0.10.0-dev", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" }
|
||||
sp-state-machine = { version = "0.10.0", default-features = false, path = "../../primitives/state-machine" }
|
||||
|
||||
@@ -138,6 +138,7 @@ impl substrate_test_client::GenesisInit for GenesisParameters {
|
||||
let state_root =
|
||||
<<<runtime::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
child_content.data.clone().into_iter().collect(),
|
||||
sp_runtime::StateVersion::V1,
|
||||
);
|
||||
let prefixed_storage_key = child_content.child_info.prefixed_storage_key();
|
||||
(prefixed_storage_key.into_inner(), state_root.encode())
|
||||
@@ -145,6 +146,7 @@ impl substrate_test_client::GenesisInit for GenesisParameters {
|
||||
let state_root =
|
||||
<<<runtime::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
storage.top.clone().into_iter().chain(child_roots).collect(),
|
||||
sp_runtime::StateVersion::V1,
|
||||
);
|
||||
let block: runtime::Block = client::genesis::construct_genesis_block(state_root);
|
||||
storage.top.extend(additional_storage_with_genesis(&block));
|
||||
|
||||
@@ -95,6 +95,7 @@ pub fn insert_genesis_block(storage: &mut Storage) -> sp_core::hash::H256 {
|
||||
let state_root =
|
||||
<<<crate::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
child_content.data.clone().into_iter().collect(),
|
||||
sp_runtime::StateVersion::V1,
|
||||
);
|
||||
(sk.clone(), state_root.encode())
|
||||
});
|
||||
@@ -102,6 +103,7 @@ pub fn insert_genesis_block(storage: &mut Storage) -> sp_core::hash::H256 {
|
||||
storage.top.extend(child_roots);
|
||||
let state_root = <<<crate::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
storage.top.clone().into_iter().collect(),
|
||||
sp_runtime::StateVersion::V1,
|
||||
);
|
||||
let block: crate::Block = genesis::construct_genesis_block(state_root);
|
||||
let genesis_hash = block.header.hash();
|
||||
|
||||
@@ -29,10 +29,7 @@ use sp_std::{marker::PhantomData, prelude::*};
|
||||
|
||||
use sp_application_crypto::{ecdsa, ed25519, sr25519, RuntimeAppPublic};
|
||||
use sp_core::{offchain::KeyTypeId, OpaqueMetadata, RuntimeDebug};
|
||||
use sp_trie::{
|
||||
trie_types::{TrieDB, TrieDBMut},
|
||||
PrefixedMemoryDB, StorageProof,
|
||||
};
|
||||
use sp_trie::{trie_types::TrieDB, PrefixedMemoryDB, StorageProof};
|
||||
use trie_db::{Trie, TrieMut};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
@@ -62,6 +59,8 @@ use sp_runtime::{
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use sp_version::NativeVersion;
|
||||
use sp_version::RuntimeVersion;
|
||||
// bench on latest state.
|
||||
use sp_trie::trie_types::TrieDBMutV1 as TrieDBMut;
|
||||
|
||||
// Ensure Babe and Aura use the same crypto to simplify things a bit.
|
||||
pub use sp_consensus_babe::{AllowedSlots, AuthorityId, Slot};
|
||||
@@ -105,6 +104,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
impl_version: 2,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
state_version: 1,
|
||||
};
|
||||
|
||||
fn version() -> RuntimeVersion {
|
||||
@@ -1268,9 +1268,9 @@ fn test_witness(proof: StorageProof, root: crate::Hash) {
|
||||
None,
|
||||
);
|
||||
assert!(ext.storage(b"value3").is_some());
|
||||
assert!(ext.storage_root().as_slice() == &root[..]);
|
||||
assert!(ext.storage_root(Default::default()).as_slice() == &root[..]);
|
||||
ext.place_storage(vec![0], Some(vec![1]));
|
||||
assert!(ext.storage_root().as_slice() != &root[..]);
|
||||
assert!(ext.storage_root(Default::default()).as_slice() != &root[..]);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1334,7 +1334,7 @@ mod tests {
|
||||
let mut root = crate::Hash::default();
|
||||
let mut mdb = sp_trie::MemoryDB::<crate::Hashing>::default();
|
||||
{
|
||||
let mut trie = sp_trie::trie_types::TrieDBMut::new(&mut mdb, &mut root);
|
||||
let mut trie = sp_trie::trie_types::TrieDBMutV1::new(&mut mdb, &mut root);
|
||||
trie.insert(b"value3", &[142]).expect("insert failed");
|
||||
trie.insert(b"value4", &[124]).expect("insert failed");
|
||||
};
|
||||
|
||||
@@ -194,9 +194,10 @@ pub fn execute_transaction(utx: Extrinsic) -> ApplyExtrinsicResult {
|
||||
|
||||
/// Finalize the block.
|
||||
pub fn finalize_block() -> Header {
|
||||
use sp_core::storage::StateVersion;
|
||||
let extrinsic_index: u32 = storage::unhashed::take(well_known_keys::EXTRINSIC_INDEX).unwrap();
|
||||
let txs: Vec<_> = (0..extrinsic_index).map(ExtrinsicData::take).collect();
|
||||
let extrinsics_root = trie::blake2_256_ordered_root(txs).into();
|
||||
let extrinsics_root = trie::blake2_256_ordered_root(txs, StateVersion::V0).into();
|
||||
let number = <Number>::take().expect("Number is set by `initialize_block`");
|
||||
let parent_hash = <ParentHash>::take();
|
||||
let mut digest = <StorageDigest>::take().expect("StorageDigest is set by `initialize_block`");
|
||||
@@ -205,8 +206,8 @@ pub fn finalize_block() -> Header {
|
||||
|
||||
// This MUST come after all changes to storage are done. Otherwise we will fail the
|
||||
// “Storage root does not match that calculated” assertion.
|
||||
let storage_root =
|
||||
Hash::decode(&mut &storage_root()[..]).expect("`storage_root` is a valid hash");
|
||||
let storage_root = Hash::decode(&mut &storage_root(StateVersion::V1)[..])
|
||||
.expect("`storage_root` is a valid hash");
|
||||
|
||||
if let Some(new_authorities) = o_new_authorities {
|
||||
digest.push(generic::DigestItem::Consensus(*b"aura", new_authorities.encode()));
|
||||
@@ -351,6 +352,7 @@ mod tests {
|
||||
Sr25519Keyring::Bob.to_raw_public(),
|
||||
Sr25519Keyring::Charlie.to_raw_public(),
|
||||
];
|
||||
|
||||
TestExternalities::new_with_code(
|
||||
wasm_binary_unwrap(),
|
||||
sp_core::storage::Storage {
|
||||
@@ -359,7 +361,7 @@ mod tests {
|
||||
twox_128(b"sys:auth").to_vec() => authorities.encode(),
|
||||
blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => {
|
||||
vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
}
|
||||
},
|
||||
],
|
||||
children_default: map![],
|
||||
},
|
||||
|
||||
@@ -157,7 +157,7 @@ where
|
||||
header.digest_mut().pop();
|
||||
let block = Block::new(header, extrinsics);
|
||||
|
||||
let (expected_spec_name, expected_spec_version) =
|
||||
let (expected_spec_name, expected_spec_version, _) =
|
||||
local_spec::<Block, ExecDispatch>(&ext, &executor);
|
||||
ensure_matching_spec::<Block>(
|
||||
block_ws_uri.clone(),
|
||||
|
||||
@@ -123,7 +123,7 @@ where
|
||||
new_ext.as_backend().root()
|
||||
);
|
||||
|
||||
let (expected_spec_name, expected_spec_version) =
|
||||
let (expected_spec_name, expected_spec_version, spec_state_version) =
|
||||
local_spec::<Block, ExecDispatch>(&new_ext, &executor);
|
||||
ensure_matching_spec::<Block>(
|
||||
command.uri.clone(),
|
||||
@@ -133,10 +133,10 @@ where
|
||||
)
|
||||
.await;
|
||||
|
||||
maybe_state_ext = Some(new_ext);
|
||||
maybe_state_ext = Some((new_ext, spec_state_version));
|
||||
}
|
||||
|
||||
let state_ext =
|
||||
let (state_ext, spec_state_version) =
|
||||
maybe_state_ext.as_mut().expect("state_ext either existed or was just created");
|
||||
|
||||
let (mut changes, encoded_result) = state_machine_call_with_proof::<Block, ExecDispatch>(
|
||||
@@ -152,7 +152,16 @@ where
|
||||
.map_err(|e| format!("failed to decode output: {:?}", e))?;
|
||||
|
||||
let storage_changes = changes
|
||||
.drain_storage_changes(&state_ext.backend, Default::default(), &mut Default::default())
|
||||
.drain_storage_changes(
|
||||
&state_ext.backend,
|
||||
Default::default(),
|
||||
&mut Default::default(),
|
||||
// Note that in case a block contains a runtime upgrade,
|
||||
// state version could potentially be incorrect here,
|
||||
// this is very niche and would only result in unaligned
|
||||
// roots, so this use case is ignored for now.
|
||||
*spec_state_version,
|
||||
)
|
||||
.unwrap();
|
||||
state_ext.backend.apply_transaction(
|
||||
storage_changes.transaction_storage_root,
|
||||
|
||||
@@ -140,7 +140,7 @@ where
|
||||
builder.build().await?
|
||||
};
|
||||
|
||||
let (expected_spec_name, expected_spec_version) =
|
||||
let (expected_spec_name, expected_spec_version, _) =
|
||||
local_spec::<Block, ExecDispatch>(&ext, &executor);
|
||||
ensure_matching_spec::<Block>(
|
||||
header_ws_uri,
|
||||
|
||||
@@ -58,7 +58,7 @@ where
|
||||
};
|
||||
|
||||
if let Some(uri) = command.state.live_uri() {
|
||||
let (expected_spec_name, expected_spec_version) =
|
||||
let (expected_spec_name, expected_spec_version, _) =
|
||||
local_spec::<Block, ExecDispatch>(&ext, &executor);
|
||||
ensure_matching_spec::<Block>(
|
||||
uri,
|
||||
|
||||
@@ -799,7 +799,7 @@ pub(crate) fn state_machine_call_with_proof<Block: BlockT, D: NativeExecutionDis
|
||||
pub(crate) fn local_spec<Block: BlockT, D: NativeExecutionDispatch + 'static>(
|
||||
ext: &TestExternalities,
|
||||
executor: &NativeElseWasmExecutor<D>,
|
||||
) -> (String, u32) {
|
||||
) -> (String, u32, sp_core::storage::StateVersion) {
|
||||
let (_, encoded) = state_machine_call::<Block, D>(
|
||||
&ext,
|
||||
&executor,
|
||||
@@ -811,6 +811,9 @@ pub(crate) fn local_spec<Block: BlockT, D: NativeExecutionDispatch + 'static>(
|
||||
.expect("all runtimes should have version; qed");
|
||||
<sp_version::RuntimeVersion as Decode>::decode(&mut &*encoded)
|
||||
.map_err(|e| format!("failed to decode output: {:?}", e))
|
||||
.map(|v| (v.spec_name.into(), v.spec_version))
|
||||
.map(|v| {
|
||||
let state_version = v.state_version();
|
||||
(v.spec_name.into(), v.spec_version, state_version)
|
||||
})
|
||||
.expect("all runtimes should have version; qed")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user