state_machine no_std witness externalities (#6934)

* checkpoint before removing CT from change trie

* before trie backend without tx

* undo

* Started no transaction, but would need using a different root
calculation method, out of the scope of this pr, will roll back.

* Remove NoTransaction.

* partially address review.
dummy stats implementation for no_std.

* Remove ChangeTrieOverlay.

* modified function

* Remove witness_ext

* need noops changes root

* update from cumulus branch

* line break

* remove warning

* line break

* From review: renamings and stats active in no std (except time).

* include cache, exclude change trie cache with individual temporary bad looking
no_std check

* little test

* fuse imports and filter_map prepare_extrinsics_input_inner fold.

* put back ExtInner into Ext, awkward double proto for new function.

* Apply suggestions from code review

* Update primitives/state-machine/Cargo.toml

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
cheme
2020-09-11 12:11:25 +02:00
committed by GitHub
parent 45d26e7419
commit 232a30fdb4
18 changed files with 1209 additions and 794 deletions
+2 -1
View File
@@ -8424,7 +8424,6 @@ version = "0.8.0-rc6"
dependencies = [
"hash-db",
"hex-literal",
"itertools 0.9.0",
"log",
"num-traits",
"parity-scale-codec",
@@ -8436,6 +8435,7 @@ dependencies = [
"sp-externalities",
"sp-panic-handler",
"sp-runtime",
"sp-std",
"sp-trie",
"trie-db",
"trie-root",
@@ -8833,6 +8833,7 @@ dependencies = [
"sp-consensus-aura",
"sp-consensus-babe",
"sp-core",
"sp-externalities",
"sp-finality-grandpa",
"sp-inherents",
"sp-io",
+13 -4
View File
@@ -13,7 +13,16 @@ documentation = "https://docs.rs/sp-externalities"
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
sp-storage = { version = "2.0.0-rc6", path = "../storage" }
sp-std = { version = "2.0.0-rc6", path = "../std" }
environmental = { version = "1.1.1" }
codec = { package = "parity-scale-codec", version = "1.3.1" }
sp-storage = { version = "2.0.0-rc6", path = "../storage", default-features = false }
sp-std = { version = "2.0.0-rc6", path = "../std", default-features = false }
environmental = { version = "1.1.1", default-features = false }
codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false }
[features]
default = ["std"]
std = [
"codec/std",
"environmental/std",
"sp-std/std",
"sp-storage/std",
]
@@ -22,7 +22,9 @@
//!
//! It is required that each extension implements the [`Extension`] trait.
use std::{collections::HashMap, collections::hash_map::Entry, any::{Any, TypeId}, ops::DerefMut};
use sp_std::{
collections::btree_map::{BTreeMap, Entry}, any::{Any, TypeId}, ops::DerefMut, boxed::Box,
};
use crate::Error;
/// Marker trait for types that should be registered as [`Externalities`](crate::Externalities) extension.
@@ -104,9 +106,10 @@ pub trait ExtensionStore {
/// Stores extensions that should be made available through the externalities.
#[derive(Default)]
pub struct Extensions {
extensions: HashMap<TypeId, Box<dyn Extension>>,
extensions: BTreeMap<TypeId, Box<dyn Extension>>,
}
#[cfg(feature = "std")]
impl std::fmt::Debug for Extensions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Extensions: ({})", self.extensions.len())
@@ -15,6 +15,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg_attr(not(feature = "std"), no_std)]
//! Substrate externalities abstraction
//!
//! The externalities mainly provide access to storage and to registered extensions. Extensions
@@ -23,7 +25,7 @@
//!
//! This crate exposes the main [`Externalities`] trait.
use std::any::{Any, TypeId};
use sp_std::{any::{Any, TypeId}, vec::Vec, boxed::Box};
use sp_storage::{ChildInfo, TrackedStorageKey};
+29 -14
View File
@@ -13,20 +13,20 @@ documentation = "https://docs.rs/sp-state-machine"
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
log = "0.4.8"
parking_lot = "0.10.0"
hash-db = "0.15.2"
trie-db = "0.22.0"
trie-root = "0.16.0"
sp-trie = { version = "2.0.0-rc6", path = "../trie" }
sp-core = { version = "2.0.0-rc6", path = "../core" }
sp-panic-handler = { version = "2.0.0-rc6", path = "../panic-handler" }
codec = { package = "parity-scale-codec", version = "1.3.1" }
num-traits = "0.2.8"
rand = "0.7.2"
sp-externalities = { version = "0.8.0-rc6", path = "../externalities" }
itertools = "0.9"
log = { version = "0.4.8", optional = true }
parking_lot = { version = "0.10.0", optional = true }
hash-db = { version = "0.15.2", default-features = false }
trie-db = { version = "0.22.0", default-features = false }
trie-root = { version = "0.16.0", default-features = false }
sp-trie = { version = "2.0.0-rc6", path = "../trie", default-features = false }
sp-core = { version = "2.0.0-rc6", path = "../core", default-features = false }
sp-panic-handler = { version = "2.0.0-rc6", path = "../panic-handler", optional = true }
codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false }
num-traits = { version = "0.2.8", default-features = false }
rand = { version = "0.7.2", optional = true }
sp-externalities = { version = "0.8.0-rc6", path = "../externalities", default-features = false }
smallvec = "1.4.1"
sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" }
[dev-dependencies]
hex-literal = "0.3.1"
@@ -34,4 +34,19 @@ sp-runtime = { version = "2.0.0-rc6", path = "../runtime" }
pretty_assertions = "0.6.1"
[features]
default = []
default = ["std"]
std = [
"codec/std",
"hash-db/std",
"num-traits/std",
"sp-core/std",
"sp-externalities/std",
"sp-std/std",
"sp-trie/std",
"trie-db/std",
"trie-root/std",
"log",
"parking_lot",
"rand",
"sp-panic-handler",
]
@@ -20,7 +20,6 @@
use hash_db::Hasher;
use codec::{Decode, Encode};
use sp_core::{
traits::RuntimeCode,
storage::{ChildInfo, well_known_keys, TrackedStorageKey}
};
use crate::{
@@ -28,12 +27,15 @@ use crate::{
trie_backend_essence::TrieBackendStorage,
UsageInfo, StorageKey, StorageValue, StorageCollection, ChildStorageCollection,
};
use sp_std::vec::Vec;
#[cfg(feature = "std")]
use sp_core::traits::RuntimeCode;
/// A state backend is used to read state data and can have changes committed
/// to it.
///
/// The clone operation (if implemented) should be cheap.
pub trait Backend<H: Hasher>: std::fmt::Debug {
pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
/// An error type when fetching data is not possible.
type Error: super::Error;
@@ -375,11 +377,13 @@ pub(crate) fn insert_into_memory_db<H, I>(mdb: &mut sp_trie::MemoryDB<H>, input:
}
/// Wrapper to create a [`RuntimeCode`] from a type that implements [`Backend`].
#[cfg(feature = "std")]
pub struct BackendRuntimeCode<'a, B, H> {
backend: &'a B,
_marker: std::marker::PhantomData<H>,
}
#[cfg(feature = "std")]
impl<'a, B: Backend<H>, H: Hasher> sp_core::traits::FetchRuntimeCode for
BackendRuntimeCode<'a, B, H>
{
@@ -388,6 +392,7 @@ impl<'a, B: Backend<H>, H: Hasher> sp_core::traits::FetchRuntimeCode for
}
}
#[cfg(feature = "std")]
impl<'a, B: Backend<H>, H: Hasher> BackendRuntimeCode<'a, B, H> where H::Out: Encode {
/// Create a new instance.
pub fn new(backend: &'a B) -> Self {
@@ -140,8 +140,15 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>(
Number: BlockNumber,
{
changes
.filter(|( _, v)| v.extrinsics().next().is_some())
.try_fold(BTreeMap::new(), |mut map: BTreeMap<&[u8], (ExtrinsicIndex<Number>, Vec<u32>)>, (k, v)| {
.filter_map(|(k, v)| {
let extrinsics = v.extrinsics();
if !extrinsics.is_empty() {
Some((k, extrinsics))
} else {
None
}
})
.try_fold(BTreeMap::new(), |mut map: BTreeMap<&[u8], (ExtrinsicIndex<Number>, Vec<u32>)>, (k, extrinsics)| {
match map.entry(k) {
Entry::Vacant(entry) => {
// ignore temporary values (values that have null value at the end of operation
@@ -161,7 +168,7 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>(
}
};
let extrinsics = v.extrinsics().cloned().collect();
let extrinsics = extrinsics.into_iter().collect();
entry.insert((ExtrinsicIndex {
block: block.clone(),
key: k.to_vec(),
@@ -170,11 +177,11 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>(
Entry::Occupied(mut entry) => {
// we do not need to check for temporary values here, because entry is Occupied
// AND we are checking it before insertion
let extrinsics = &mut entry.get_mut().1;
extrinsics.extend(
v.extrinsics().cloned()
let entry_extrinsics = &mut entry.get_mut().1;
entry_extrinsics.extend(
extrinsics.into_iter()
);
extrinsics.sort();
entry_extrinsics.sort();
},
}
@@ -85,9 +85,6 @@ use crate::{
},
};
/// Changes that are made outside of extrinsics are marked with this index;
pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff;
/// Requirements for block number that can be used with changes tries.
pub trait BlockNumber:
Send + Sync + 'static +
@@ -17,7 +17,7 @@
/// State Machine Errors
use std::fmt;
use sp_std::fmt;
/// State Machine Error bound.
///
@@ -34,7 +34,7 @@ impl<T: 'static + fmt::Debug + fmt::Display + Send> Error for T {}
#[derive(Debug, Eq, PartialEq)]
pub enum ExecutionError {
/// Backend error.
Backend(String),
Backend(crate::DefaultError),
/// The entry `:code` doesn't exist in storage so there's no way we can execute anything.
CodeEntryDoesNotExist,
/// Backend is incompatible with execution proof generation process.
+114 -36
View File
@@ -18,23 +18,28 @@
//! Concrete externalities implementation.
use crate::{
StorageKey, StorageValue, OverlayedChanges, StorageTransactionCache,
StorageKey, StorageValue, OverlayedChanges,
backend::Backend,
changes_trie::State as ChangesTrieState,
};
use hash_db::Hasher;
use sp_core::{
offchain::storage::OffchainOverlayedChanges,
storage::{well_known_keys::is_child_storage_key, ChildInfo, TrackedStorageKey},
traits::Externalities, hexdisplay::HexDisplay,
hexdisplay::HexDisplay,
};
use sp_trie::{trie_types::Layout, empty_child_trie_root};
use sp_externalities::{Extensions, Extension};
use sp_externalities::{Externalities, Extensions, Extension,
ExtensionStore};
use codec::{Decode, Encode, EncodeAppend};
use std::{error, fmt, any::{Any, TypeId}};
use log::{warn, trace};
use sp_std::{fmt, any::{Any, TypeId}, vec::Vec, vec, boxed::Box};
use crate::{warn, trace, log_error};
#[cfg(feature = "std")]
use sp_core::offchain::storage::OffchainOverlayedChanges;
#[cfg(feature = "std")]
use crate::changes_trie::State as ChangesTrieState;
use crate::StorageTransactionCache;
#[cfg(feature = "std")]
use std::error;
const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime";
const BENCHMARKING_FN: &str = "\
@@ -42,7 +47,19 @@ const BENCHMARKING_FN: &str = "\
For that reason client started transactions before calling into runtime are not allowed.
Without client transactions the loop condition garantuees the success of the tx close.";
#[cfg(feature = "std")]
fn guard() -> sp_panic_handler::AbortGuard {
sp_panic_handler::AbortGuard::force_abort()
}
#[cfg(not(feature = "std"))]
fn guard() -> () {
()
}
/// Errors that can occur when interacting with the externalities.
#[cfg(feature = "std")]
#[derive(Debug, Copy, Clone)]
pub enum Error<B, E> {
/// Failure to load state data from the backend.
@@ -53,6 +70,7 @@ pub enum Error<B, E> {
Executor(E),
}
#[cfg(feature = "std")]
impl<B: fmt::Display, E: fmt::Display> fmt::Display for Error<B, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
@@ -62,6 +80,7 @@ impl<B: fmt::Display, E: fmt::Display> fmt::Display for Error<B, E> {
}
}
#[cfg(feature = "std")]
impl<B: error::Error, E: error::Error> error::Error for Error<B, E> {
fn description(&self) -> &str {
match *self {
@@ -81,30 +100,49 @@ pub struct Ext<'a, H, N, B>
/// The overlayed changes to write to.
overlay: &'a mut OverlayedChanges,
/// The overlayed changes destined for the Offchain DB.
#[cfg(feature = "std")]
offchain_overlay: &'a mut OffchainOverlayedChanges,
/// The storage backend to read from.
backend: &'a B,
/// The cache for the storage transactions.
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H, N>,
/// Changes trie state to read from.
#[cfg(feature = "std")]
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
/// Pseudo-unique id used for tracing.
pub id: u16,
/// Dummy usage of N arg.
_phantom: std::marker::PhantomData<N>,
_phantom: sp_std::marker::PhantomData<N>,
/// Extensions registered with this instance.
#[cfg(feature = "std")]
extensions: Option<&'a mut Extensions>,
}
impl<'a, H, N, B> Ext<'a, H, N, B>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
B: 'a + Backend<H>,
N: crate::changes_trie::BlockNumber,
where
H: Hasher,
B: Backend<H>,
N: crate::changes_trie::BlockNumber,
{
/// Create a new `Ext`.
#[cfg(not(feature = "std"))]
pub fn new(
overlay: &'a mut OverlayedChanges,
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H, N>,
backend: &'a B,
) -> Self {
Ext {
overlay,
backend,
id: 0,
storage_transaction_cache,
_phantom: Default::default(),
}
}
/// Create a new `Ext` from overlayed changes and read-only backend
#[cfg(feature = "std")]
pub fn new(
overlay: &'a mut OverlayedChanges,
offchain_overlay: &'a mut OffchainOverlayedChanges,
@@ -133,6 +171,7 @@ where
}
/// Read only accessor for the scheduled overlay changes.
#[cfg(feature = "std")]
pub fn get_offchain_storage_changes(&self) -> &OffchainOverlayedChanges {
&*self.offchain_overlay
}
@@ -159,14 +198,14 @@ where
}
}
impl<'a, H, B, N> Externalities for Ext<'a, H, N, B>
impl<'a, H, N, B> Externalities for Ext<'a, H, N, B>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
B: 'a + Backend<H>,
B: Backend<H>,
N: crate::changes_trie::BlockNumber,
{
#[cfg(feature = "std")]
fn set_offchain_storage(&mut self, key: &[u8], value: Option<&[u8]>) {
use ::sp_core::offchain::STORAGE_PREFIX;
match value {
@@ -175,8 +214,11 @@ where
}
}
#[cfg(not(feature = "std"))]
fn set_offchain_storage(&mut self, _key: &[u8], _value: Option<&[u8]>) {}
fn storage(&self, key: &[u8]) -> Option<StorageValue> {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
let result = self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL));
trace!(target: "state", "{:04x}: Get {}={:?}",
@@ -188,7 +230,7 @@ where
}
fn storage_hash(&self, key: &[u8]) -> Option<Vec<u8>> {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
let result = self.overlay
.storage(key)
.map(|x| x.map(|x| H::hash(x)))
@@ -207,7 +249,7 @@ where
child_info: &ChildInfo,
key: &[u8],
) -> Option<StorageValue> {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
let result = self.overlay
.child_storage(child_info, key)
.map(|x| x.map(|x| x.to_vec()))
@@ -231,7 +273,7 @@ where
child_info: &ChildInfo,
key: &[u8],
) -> Option<Vec<u8>> {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
let result = self.overlay
.child_storage(child_info, key)
.map(|x| x.map(|x| H::hash(x)))
@@ -251,7 +293,7 @@ where
}
fn exists_storage(&self, key: &[u8]) -> bool {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
let result = match self.overlay.storage(key) {
Some(x) => x.is_some(),
_ => self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL),
@@ -271,7 +313,7 @@ where
child_info: &ChildInfo,
key: &[u8],
) -> bool {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
let result = match self.overlay.child_storage(child_info, key) {
Some(x) => x.is_some(),
@@ -337,7 +379,7 @@ where
HexDisplay::from(&key),
value.as_ref().map(HexDisplay::from)
);
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
if is_child_storage_key(&key) {
warn!(target: "trie", "Refuse to directly set child storage key");
return;
@@ -359,7 +401,7 @@ where
HexDisplay::from(&key),
value.as_ref().map(HexDisplay::from)
);
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
self.mark_dirty();
self.overlay.set_child_storage(child_info, key, value);
@@ -373,7 +415,7 @@ where
self.id,
HexDisplay::from(&child_info.storage_key()),
);
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
self.mark_dirty();
self.overlay.clear_child_storage(child_info);
@@ -387,7 +429,7 @@ where
self.id,
HexDisplay::from(&prefix),
);
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
if is_child_storage_key(prefix) {
warn!(target: "trie", "Refuse to directly clear prefix that is part of child storage key");
return;
@@ -410,7 +452,7 @@ where
HexDisplay::from(&child_info.storage_key()),
HexDisplay::from(&prefix),
);
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
self.mark_dirty();
self.overlay.clear_child_prefix(child_info, prefix);
@@ -430,7 +472,7 @@ where
HexDisplay::from(&value),
);
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
self.mark_dirty();
let backend = &mut self.backend;
@@ -446,7 +488,7 @@ where
}
fn storage_root(&mut self) -> Vec<u8> {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
if let Some(ref root) = self.storage_transaction_cache.transaction_storage_root {
trace!(target: "state", "{:04x}: Root(cached) {}",
self.id,
@@ -464,7 +506,7 @@ where
&mut self,
child_info: &ChildInfo,
) -> Vec<u8> {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
let storage_key = child_info.storage_key();
let prefixed_storage_key = child_info.prefixed_storage_key();
if self.storage_transaction_cache.transaction_storage_root.is_some() {
@@ -525,8 +567,14 @@ where
}
}
#[cfg(not(feature = "std"))]
fn storage_changes_root(&mut self, _parent_hash: &[u8]) -> Result<Option<Vec<u8>>, ()> {
Ok(None)
}
#[cfg(feature = "std")]
fn storage_changes_root(&mut self, parent_hash: &[u8]) -> Result<Option<Vec<u8>>, ()> {
let _guard = sp_panic_handler::AbortGuard::force_abort();
let _guard = guard();
let root = self.overlay.changes_trie_root(
self.backend,
self.changes_trie_state.as_ref(),
@@ -569,6 +617,7 @@ where
}
self.overlay.drain_storage_changes(
&self.backend,
#[cfg(feature = "std")]
None,
Default::default(),
self.storage_transaction_cache,
@@ -586,6 +635,7 @@ where
}
let changes = self.overlay.drain_storage_changes(
&self.backend,
#[cfg(feature = "std")]
None,
Default::default(),
self.storage_transaction_cache,
@@ -619,7 +669,6 @@ where
}
}
/// Implement `Encode` by forwarding the stored raw vec.
struct EncodeOpaqueValue(Vec<u8>);
@@ -644,12 +693,12 @@ impl<'a> StorageAppend<'a> {
pub fn append(&mut self, value: Vec<u8>) {
let value = vec![EncodeOpaqueValue(value)];
let item = std::mem::take(self.0);
let item = sp_std::mem::take(self.0);
*self.0 = match Vec::<EncodeOpaqueValue>::append_or_new(item, &value) {
Ok(item) => item,
Err(_) => {
log::error!(
log_error!(
target: "runtime",
"Failed to append value, resetting storage item to `[value]`.",
);
@@ -659,7 +708,36 @@ impl<'a> StorageAppend<'a> {
}
}
impl<'a, H, B, N> sp_externalities::ExtensionStore for Ext<'a, H, N, B>
#[cfg(not(feature = "std"))]
impl<'a, H, N, B> ExtensionStore for Ext<'a, H, N, B>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
B: Backend<H>,
N: crate::changes_trie::BlockNumber,
{
fn extension_by_type_id(&mut self, _type_id: TypeId) -> Option<&mut dyn Any> {
None
}
fn register_extension_with_type_id(
&mut self,
_type_id: TypeId,
_extension: Box<dyn Extension>,
) -> Result<(), sp_externalities::Error> {
Err(sp_externalities::Error::ExtensionsAreNotSupported)
}
fn deregister_extension_by_type_id(
&mut self,
_type_id: TypeId,
) -> Result<(), sp_externalities::Error> {
Err(sp_externalities::Error::ExtensionsAreNotSupported)
}
}
#[cfg(feature = "std")]
impl<'a, H, N, B> ExtensionStore for Ext<'a, H, N, B>
where
H: Hasher,
B: 'a + Backend<H>,
File diff suppressed because it is too large Load Diff
@@ -17,18 +17,22 @@
//! Houses the code that implements the transactional overlay storage.
use super::{StorageKey, StorageValue};
use super::{StorageKey, StorageValue, Extrinsics};
use itertools::Itertools;
use std::collections::{HashSet, BTreeMap, BTreeSet};
#[cfg(feature = "std")]
use std::collections::HashSet as Set;
#[cfg(not(feature = "std"))]
use sp_std::collections::btree_set::BTreeSet as Set;
use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
use smallvec::SmallVec;
use log::warn;
use crate::warn;
const PROOF_OVERLAY_NON_EMPTY: &str = "\
An OverlayValue is always created with at least one transaction and dropped as soon
as the last transaction is removed; qed";
type DirtyKeysSets = SmallVec<[HashSet<StorageKey>; 5]>;
type DirtyKeysSets = SmallVec<[Set<StorageKey>; 5]>;
type Transactions = SmallVec<[InnerValue; 5]>;
/// Error returned when trying to commit or rollback while no transaction is open or
@@ -63,7 +67,7 @@ struct InnerValue {
value: Option<StorageValue>,
/// The set of extrinsic indices where the values has been changed.
/// Is filled only if runtime has announced changes trie support.
extrinsics: BTreeSet<u32>,
extrinsics: Extrinsics,
}
/// An overlay that contains all versions of a value for a specific key.
@@ -105,8 +109,10 @@ impl OverlayedValue {
}
/// Unique list of extrinsic indices which modified the value.
pub fn extrinsics(&self) -> impl Iterator<Item=&u32> {
self.transactions.iter().flat_map(|t| t.extrinsics.iter()).unique()
pub fn extrinsics(&self) -> BTreeSet<u32> {
let mut set = BTreeSet::new();
self.transactions.iter().for_each(|t| t.extrinsics.copy_extrinsics_into(&mut set));
set
}
/// Mutable reference to the most recent version.
@@ -120,7 +126,7 @@ impl OverlayedValue {
}
/// Mutable reference to the set which holds the indices for the **current transaction only**.
fn transaction_extrinsics_mut(&mut self) -> &mut BTreeSet<u32> {
fn transaction_extrinsics_mut(&mut self) -> &mut Extrinsics {
&mut self.transactions.last_mut().expect(PROOF_OVERLAY_NON_EMPTY).extrinsics
}
@@ -163,9 +169,9 @@ impl OverlayedChangeSet {
/// This changeset might be created when there are already open transactions.
/// We need to catch up here so that the child is at the same transaction depth.
pub fn spawn_child(&self) -> Self {
use std::iter::repeat;
use sp_std::iter::repeat;
Self {
dirty_keys: repeat(HashSet::new()).take(self.transaction_depth()).collect(),
dirty_keys: repeat(Set::new()).take(self.transaction_depth()).collect(),
num_client_transactions: self.num_client_transactions,
execution_mode: self.execution_mode,
.. Default::default()
@@ -232,7 +238,7 @@ impl OverlayedChangeSet {
at_extrinsic: Option<u32>,
) {
for (key, val) in self.changes.iter_mut().filter(|(k, v)| predicate(k, v)) {
val.set(None, insert_dirty(&mut self.dirty_keys, key.to_owned()), at_extrinsic);
val.set(None, insert_dirty(&mut self.dirty_keys, key.clone()), at_extrinsic);
}
}
@@ -243,7 +249,7 @@ impl OverlayedChangeSet {
/// Get the change that is next to the supplied key.
pub fn next_change(&self, key: &[u8]) -> Option<(&[u8], &OverlayedValue)> {
use std::ops::Bound;
use sp_std::ops::Bound;
let range = (Bound::Excluded(key), Bound::Unbounded);
self.changes.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v))
}
@@ -388,7 +394,7 @@ mod test {
fn assert_changes(is: &OverlayedChangeSet, expected: &Changes) {
let is: Changes = is.changes().map(|(k, v)| {
(k.as_ref(), (v.value().map(AsRef::as_ref), v.extrinsics().cloned().collect()))
(k.as_ref(), (v.value().map(AsRef::as_ref), v.extrinsics().into_iter().collect()))
}).collect();
assert_eq!(&is, expected);
}
@@ -20,23 +20,38 @@
mod changeset;
use crate::{
backend::Backend, ChangesTrieTransaction,
changes_trie::{
NO_EXTRINSIC_INDEX, BlockNumber, build_changes_trie,
State as ChangesTrieState,
},
backend::Backend,
stats::StateMachineStats,
};
use sp_std::vec::Vec;
use self::changeset::OverlayedChangeSet;
use std::collections::HashMap;
#[cfg(feature = "std")]
use crate::{
ChangesTrieTransaction,
changes_trie::{
build_changes_trie,
State as ChangesTrieState,
},
};
use crate::changes_trie::BlockNumber;
#[cfg(feature = "std")]
use std::collections::HashMap as Map;
#[cfg(not(feature = "std"))]
use sp_std::collections::btree_map::BTreeMap as Map;
use sp_std::collections::btree_set::BTreeSet;
use codec::{Decode, Encode};
use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo};
#[cfg(feature = "std")]
use sp_core::offchain::storage::OffchainOverlayedChanges;
use hash_db::Hasher;
use crate::DefaultError;
pub use self::changeset::{OverlayedValue, NoOpenTransaction, AlreadyInRuntime, NotInRuntime};
/// Changes that are made outside of extrinsics are marked with this index;
pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff;
/// Storage key.
pub type StorageKey = Vec<u8>;
@@ -49,6 +64,29 @@ pub type StorageCollection = Vec<(StorageKey, Option<StorageValue>)>;
/// In memory arrays of storage values for multiple child tries.
pub type ChildStorageCollection = Vec<(StorageKey, StorageCollection)>;
/// Keep trace of extrinsics index for a modified value.
#[derive(Debug, Default, Eq, PartialEq, Clone)]
pub struct Extrinsics(Vec<u32>);
impl Extrinsics {
/// Extracts extrinsics into a `BTreeSets`.
fn copy_extrinsics_into(&self, dest: &mut BTreeSet<u32>) {
dest.extend(self.0.iter())
}
/// Add an extrinsics.
fn insert(&mut self, ext: u32) {
if Some(&ext) != self.0.last() {
self.0.push(ext);
}
}
/// Extend `self` with `other`.
fn extend(&mut self, other: Self) {
self.0.extend(other.0.into_iter());
}
}
/// The set of changes that are overlaid onto the backend.
///
/// It allows changes to be modified using nestable transactions.
@@ -57,7 +95,7 @@ pub struct OverlayedChanges {
/// Top level storage changes.
top: OverlayedChangeSet,
/// Child storage changes. The map key is the child storage key without the common prefix.
children: HashMap<StorageKey, (OverlayedChangeSet, ChildInfo)>,
children: Map<StorageKey, (OverlayedChangeSet, ChildInfo)>,
/// True if extrinsics stats must be collected.
collect_extrinsics: bool,
/// Collect statistic on this execution.
@@ -76,6 +114,7 @@ pub struct StorageChanges<Transaction, H: Hasher, N: BlockNumber> {
/// All changes to the child storages.
pub child_storage_changes: ChildStorageCollection,
/// Offchain state changes to write to the offchain database.
#[cfg(feature = "std")]
pub offchain_storage_changes: OffchainOverlayedChanges,
/// A transaction for the backend that contains all changes from
/// [`main_storage_changes`](StorageChanges::main_storage_changes) and from
@@ -87,9 +126,14 @@ pub struct StorageChanges<Transaction, H: Hasher, N: BlockNumber> {
/// Contains the transaction for the backend for the changes trie.
///
/// If changes trie is disabled the value is set to `None`.
#[cfg(feature = "std")]
pub changes_trie_transaction: Option<ChangesTrieTransaction<H, N>>,
/// Phantom data for block number until change trie support no_std.
#[cfg(not(feature = "std"))]
pub _ph: sp_std::marker::PhantomData<N>,
}
#[cfg(feature = "std")]
impl<Transaction, H: Hasher, N: BlockNumber> StorageChanges<Transaction, H, N> {
/// Deconstruct into the inner values
pub fn into_inner(self) -> (
@@ -120,9 +164,14 @@ pub struct StorageTransactionCache<Transaction, H: Hasher, N: BlockNumber> {
/// The storage root after applying the transaction.
pub(crate) transaction_storage_root: Option<H::Out>,
/// Contains the changes trie transaction.
#[cfg(feature = "std")]
pub(crate) changes_trie_transaction: Option<Option<ChangesTrieTransaction<H, N>>>,
/// The storage root after applying the changes trie transaction.
#[cfg(feature = "std")]
pub(crate) changes_trie_transaction_storage_root: Option<Option<H::Out>>,
/// Phantom data for block number until change trie support no_std.
#[cfg(not(feature = "std"))]
pub(crate) _ph: sp_std::marker::PhantomData<N>,
}
impl<Transaction, H: Hasher, N: BlockNumber> StorageTransactionCache<Transaction, H, N> {
@@ -137,8 +186,12 @@ impl<Transaction, H: Hasher, N: BlockNumber> Default for StorageTransactionCache
Self {
transaction: None,
transaction_storage_root: None,
#[cfg(feature = "std")]
changes_trie_transaction: None,
#[cfg(feature = "std")]
changes_trie_transaction_storage_root: None,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
}
}
}
@@ -148,10 +201,14 @@ impl<Transaction: Default, H: Hasher, N: BlockNumber> Default for StorageChanges
Self {
main_storage_changes: Default::default(),
child_storage_changes: Default::default(),
#[cfg(feature = "std")]
offchain_storage_changes: Default::default(),
transaction: Default::default(),
transaction_storage_root: Default::default(),
#[cfg(feature = "std")]
changes_trie_transaction: None,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
}
}
}
@@ -190,7 +247,7 @@ impl OverlayedChanges {
key: &[u8],
init: impl Fn() -> StorageValue,
) -> &mut StorageValue {
let value = self.top.modify(key.to_owned(), init, self.extrinsic_index());
let value = self.top.modify(key.to_vec(), init, self.extrinsic_index());
// if the value was deleted initialise it back with an empty vec
value.get_or_insert_with(StorageValue::default)
@@ -235,7 +292,7 @@ impl OverlayedChanges {
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
(
top.spawn_child(),
child_info.to_owned()
child_info.clone()
)
);
let updatable = info.try_update(child_info);
@@ -256,7 +313,7 @@ impl OverlayedChanges {
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
(
top.spawn_child(),
child_info.to_owned()
child_info.clone()
)
);
let updatable = info.try_update(child_info);
@@ -285,7 +342,7 @@ impl OverlayedChanges {
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
(
top.spawn_child(),
child_info.to_owned()
child_info.clone()
)
);
let updatable = info.try_update(child_info);
@@ -322,7 +379,7 @@ impl OverlayedChanges {
/// there is no open transaction that can be rolled back.
pub fn rollback_transaction(&mut self) -> Result<(), NoOpenTransaction> {
self.top.rollback_transaction()?;
self.children.retain(|_, (changeset, _)| {
retain_map(&mut self.children, |_, (changeset, _)| {
changeset.rollback_transaction()
.expect("Top and children changesets are started in lockstep; qed");
!changeset.is_empty()
@@ -379,7 +436,7 @@ impl OverlayedChanges {
impl Iterator<Item=(StorageKey, Option<StorageValue>)>,
impl Iterator<Item=(StorageKey, (impl Iterator<Item=(StorageKey, Option<StorageValue>)>, ChildInfo))>,
) {
use std::mem::take;
use sp_std::mem::take;
(
take(&mut self.top).drain_commited(),
take(&mut self.children).into_iter()
@@ -409,6 +466,7 @@ impl OverlayedChanges {
}
/// Convert this instance with all changes into a [`StorageChanges`] instance.
#[cfg(feature = "std")]
pub fn into_storage_changes<
B: Backend<H>, H: Hasher, N: BlockNumber
>(
@@ -417,7 +475,8 @@ impl OverlayedChanges {
changes_trie_state: Option<&ChangesTrieState<H, N>>,
parent_hash: H::Out,
mut cache: StorageTransactionCache<B::Transaction, H, N>,
) -> Result<StorageChanges<B::Transaction, H, N>, String> where H::Out: Ord + Encode + 'static {
) -> Result<StorageChanges<B::Transaction, H, N>, DefaultError>
where H::Out: Ord + Encode + 'static {
self.drain_storage_changes(backend, changes_trie_state, parent_hash, &mut cache)
}
@@ -425,10 +484,12 @@ impl OverlayedChanges {
pub fn drain_storage_changes<B: Backend<H>, H: Hasher, N: BlockNumber>(
&mut self,
backend: &B,
#[cfg(feature = "std")]
changes_trie_state: Option<&ChangesTrieState<H, N>>,
parent_hash: H::Out,
mut cache: &mut StorageTransactionCache<B::Transaction, H, N>,
) -> Result<StorageChanges<B::Transaction, H, N>, String> where H::Out: Ord + Encode + 'static {
) -> Result<StorageChanges<B::Transaction, H, N>, 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);
@@ -439,6 +500,7 @@ impl OverlayedChanges {
.expect("Transaction was be generated as part of `storage_root`; qed");
// If the transaction does not exist, we generate it.
#[cfg(feature = "std")]
if cache.changes_trie_transaction.is_none() {
self.changes_trie_root(
backend,
@@ -449,20 +511,24 @@ impl OverlayedChanges {
).map_err(|_| "Failed to generate changes trie transaction")?;
}
#[cfg(feature = "std")]
let changes_trie_transaction = cache.changes_trie_transaction
.take()
.expect("Changes trie transaction was generated by `changes_trie_root`; qed");
let offchain_storage_changes = Default::default();
let (main_storage_changes, child_storage_changes) = self.drain_committed();
Ok(StorageChanges {
main_storage_changes: main_storage_changes.collect(),
child_storage_changes: child_storage_changes.map(|(sk, it)| (sk, it.0.collect())).collect(),
offchain_storage_changes,
#[cfg(feature = "std")]
offchain_storage_changes: Default::default(),
transaction,
transaction_storage_root,
#[cfg(feature = "std")]
changes_trie_transaction,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
})
}
@@ -520,6 +586,7 @@ impl OverlayedChanges {
/// # Panics
///
/// Panics on storage error, when `panic_on_storage_error` is set.
#[cfg(feature = "std")]
pub fn changes_trie_root<'a, H: Hasher, N: BlockNumber, B: Backend<H>>(
&self,
backend: &B,
@@ -563,6 +630,29 @@ impl OverlayedChanges {
}
}
#[cfg(feature = "std")]
fn retain_map<K, V, F>(map: &mut Map<K, V>, f: F)
where
K: std::cmp::Eq + std::hash::Hash,
F: FnMut(&K, &mut V) -> bool,
{
map.retain(f);
}
#[cfg(not(feature = "std"))]
fn retain_map<K, V, F>(map: &mut Map<K, V>, mut f: F)
where
K: Ord,
F: FnMut(&K, &mut V) -> bool,
{
let old = sp_std::mem::replace(map, Map::default());
for (k, mut v) in old.into_iter() {
if f(&k, &mut v) {
map.insert(k, v);
}
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
@@ -578,7 +668,7 @@ mod tests {
expected: Vec<u32>,
) {
assert_eq!(
overlay.get(key.as_ref()).unwrap().extrinsics().cloned().collect::<Vec<_>>(),
overlay.get(key.as_ref()).unwrap().extrinsics().into_iter().collect::<Vec<_>>(),
expected
)
}
@@ -17,8 +17,9 @@
//! Usage statistics for state db
#[cfg(feature = "std")]
use std::time::{Instant, Duration};
use std::cell::RefCell;
use sp_std::cell::RefCell;
/// Measured count of operations and total bytes.
#[derive(Clone, Debug, Default)]
@@ -50,8 +51,10 @@ pub struct UsageInfo {
/// Memory used.
pub memory: usize,
#[cfg(feature = "std")]
/// Moment at which current statistics has been started being collected.
pub started: Instant,
#[cfg(feature = "std")]
/// Timespan of the statistics.
pub span: Duration,
}
@@ -99,7 +102,9 @@ impl UsageInfo {
cache_reads: UsageUnit::default(),
modified_reads: UsageUnit::default(),
memory: 0,
#[cfg(feature = "std")]
started: Instant::now(),
#[cfg(feature = "std")]
span: Default::default(),
}
}
@@ -17,7 +17,7 @@
//! Trie-based state machine backend.
use log::{warn, debug};
use crate::{warn, debug};
use hash_db::Hasher;
use sp_trie::{Trie, delta_trie_root, empty_child_trie_root, child_delta_trie_root};
use sp_trie::trie_types::{TrieDB, TrieError, Layout};
@@ -27,6 +27,7 @@ use crate::{
StorageKey, StorageValue, Backend,
trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral},
};
use sp_std::{boxed::Box, vec::Vec};
/// Patricia trie-based backend. Transaction type is an overlay of changes to commit.
pub struct TrieBackend<S: TrieBackendStorage<H>, H: Hasher> {
@@ -67,8 +68,8 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackend<S, H> where H::Out: Codec
}
}
impl<S: TrieBackendStorage<H>, H: Hasher> std::fmt::Debug for TrieBackend<S, H> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<S: TrieBackendStorage<H>, H: Hasher> sp_std::fmt::Debug for TrieBackend<S, H> {
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
write!(f, "TrieBackend")
}
}
@@ -76,7 +77,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> std::fmt::Debug for TrieBackend<S, H>
impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
H::Out: Ord + Codec,
{
type Error = String;
type Error = crate::DefaultError;
type Transaction = S::Overlay;
type TrieBackendStorage = S;
@@ -18,9 +18,10 @@
//! Trie-based state machine backend essence used to read values
//! from storage.
use std::ops::Deref;
#[cfg(feature = "std")]
use std::sync::Arc;
use log::{debug, warn};
use sp_std::{ops::Deref, boxed::Box, vec::Vec};
use crate::{warn, debug};
use hash_db::{self, Hasher, Prefix};
use sp_trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue,
empty_child_trie_root, read_trie_value, read_child_trie_value,
@@ -30,10 +31,19 @@ use crate::{backend::Consolidate, StorageKey, StorageValue};
use sp_core::storage::ChildInfo;
use codec::Encode;
#[cfg(not(feature = "std"))]
macro_rules! format {
($($arg:tt)+) => (
crate::DefaultError
);
}
type Result<V> = sp_std::result::Result<V, crate::DefaultError>;
/// Patricia trie-based storage trait.
pub trait Storage<H: Hasher>: Send + Sync {
/// Get a trie node.
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String>;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>>;
}
/// Patricia trie-based pairs storage essence.
@@ -80,12 +90,12 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
/// Return the next key in the trie i.e. the minimum key that is strictly superior to `key` in
/// lexicographic order.
pub fn next_storage_key(&self, key: &[u8]) -> Result<Option<StorageKey>, String> {
pub fn next_storage_key(&self, key: &[u8]) -> Result<Option<StorageKey>> {
self.next_storage_key_from_root(&self.root, None, key)
}
/// Access the root of the child storage in its parent trie
fn child_root(&self, child_info: &ChildInfo) -> Result<Option<StorageValue>, String> {
fn child_root(&self, child_info: &ChildInfo) -> Result<Option<StorageValue>> {
self.storage(child_info.prefixed_storage_key().as_slice())
}
@@ -95,7 +105,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<StorageKey>, String> {
) -> Result<Option<StorageKey>> {
let child_root = match self.child_root(child_info)? {
Some(child_root) => child_root,
None => return Ok(None),
@@ -118,7 +128,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
root: &H::Out,
child_info: Option<&ChildInfo>,
key: &[u8],
) -> Result<Option<StorageKey>, String> {
) -> Result<Option<StorageKey>> {
let dyn_eph: &dyn hash_db::HashDBRef<_, _>;
let keyspace_eph;
if let Some(child_info) = child_info.as_ref() {
@@ -158,7 +168,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
}
/// Get the value of storage at given key.
pub fn storage(&self, key: &[u8]) -> Result<Option<StorageValue>, String> {
pub fn storage(&self, key: &[u8]) -> Result<Option<StorageValue>> {
let map_e = |e| format!("Trie lookup error: {}", e);
read_trie_value::<Layout<H>, _>(self, &self.root, key).map_err(map_e)
@@ -169,7 +179,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<StorageValue>, String> {
) -> Result<Option<StorageValue>> {
let root = self.child_root(child_info)?
.unwrap_or_else(|| empty_child_trie_root::<Layout<H>>().encode());
@@ -234,7 +244,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
mut f: F,
child_info: Option<&ChildInfo>,
) {
let mut iter = move |db| -> Result<(), Box<TrieError<H::Out>>> {
let mut iter = move |db| -> sp_std::result::Result<(), Box<TrieError<H::Out>>> {
let trie = TrieDB::<H>::new(db, root)?;
for x in TrieDBIterator::new_prefixed(&trie, prefix)? {
@@ -337,14 +347,15 @@ pub trait TrieBackendStorage<H: Hasher>: Send + Sync {
/// Type of in-memory overlay.
type Overlay: hash_db::HashDB<H, DBValue> + Default + Consolidate;
/// Get the value stored at key.
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String>;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>>;
}
// This implementation is used by normal storage trie clients.
#[cfg(feature = "std")]
impl<H: Hasher> TrieBackendStorage<H> for Arc<dyn Storage<H>> {
type Overlay = PrefixedMemoryDB<H>;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
Storage::<H>::get(self.deref(), key, prefix)
}
}
@@ -353,7 +364,7 @@ impl<H: Hasher> TrieBackendStorage<H> for Arc<dyn Storage<H>> {
impl<H: Hasher> TrieBackendStorage<H> for PrefixedMemoryDB<H> {
type Overlay = PrefixedMemoryDB<H>;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
Ok(hash_db::HashDB::get(self, key, prefix))
}
}
@@ -361,7 +372,7 @@ impl<H: Hasher> TrieBackendStorage<H> for PrefixedMemoryDB<H> {
impl<H: Hasher> TrieBackendStorage<H> for MemoryDB<H> {
type Overlay = MemoryDB<H>;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
Ok(hash_db::HashDB::get(self, key, prefix))
}
}
+4 -1
View File
@@ -42,6 +42,8 @@ sp-transaction-pool = { version = "2.0.0-rc6", default-features = false, path =
trie-db = { version = "0.22.0", default-features = false }
parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] }
sc-service = { version = "0.8.0-rc6", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" }
sp-state-machine = { version = "0.8.0-rc6", default-features = false, path = "../../primitives/state-machine" }
sp-externalities = { version = "0.8.0-rc6", default-features = false, path = "../../primitives/externalities" }
# 3rd party
cfg-if = "0.1.10"
@@ -52,7 +54,6 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] }
sc-block-builder = { version = "0.8.0-rc6", path = "../../client/block-builder" }
sc-executor = { version = "0.8.0-rc6", path = "../../client/executor" }
substrate-test-runtime-client = { version = "2.0.0-rc6", path = "./client" }
sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" }
[build-dependencies]
wasm-builder-runner = { version = "1.0.5", package = "substrate-wasm-builder-runner", path = "../../utils/wasm-builder-runner" }
@@ -84,6 +85,8 @@ std = [
"sp-session/std",
"sp-api/std",
"sp-runtime/std",
"sp-externalities/std",
"sp-state-machine/std",
"pallet-babe/std",
"frame-system-rpc-runtime-api/std",
"frame-system/std",
+70 -1
View File
@@ -29,7 +29,7 @@ use codec::{Encode, Decode, Input, Error};
use sp_core::{offchain::KeyTypeId, ChangesTrieConfiguration, OpaqueMetadata, RuntimeDebug};
use sp_application_crypto::{ed25519, sr25519, ecdsa, RuntimeAppPublic};
use trie_db::{TrieMut, Trie};
use sp_trie::PrefixedMemoryDB;
use sp_trie::{PrefixedMemoryDB, StorageProof};
use sp_trie::trie_types::{TrieDB, TrieDBMut};
use sp_api::{decl_runtime_apis, impl_runtime_apis};
@@ -335,6 +335,8 @@ cfg_if! {
fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic);
/// Run various tests against storage.
fn test_storage();
/// Check a witness.
fn test_witness(proof: StorageProof, root: crate::Hash);
/// Test that ensures that we can call a function that takes multiple
/// arguments.
fn test_multiple_arguments(data: Vec<u8>, other: Vec<u8>, num: u32);
@@ -384,6 +386,8 @@ cfg_if! {
fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic);
/// Run various tests against storage.
fn test_storage();
/// Check a witness.
fn test_witness(proof: StorageProof, root: crate::Hash);
/// Test that ensures that we can call a function that takes multiple
/// arguments.
fn test_multiple_arguments(data: Vec<u8>, other: Vec<u8>, num: u32);
@@ -684,6 +688,10 @@ cfg_if! {
test_read_child_storage();
}
fn test_witness(proof: StorageProof, root: crate::Hash) {
test_witness(proof, root);
}
fn test_multiple_arguments(data: Vec<u8>, other: Vec<u8>, num: u32) {
assert_eq!(&data[..], &other[..]);
assert_eq!(data.len(), num as usize);
@@ -926,6 +934,10 @@ cfg_if! {
test_read_child_storage();
}
fn test_witness(proof: StorageProof, root: crate::Hash) {
test_witness(proof, root);
}
fn test_multiple_arguments(data: Vec<u8>, other: Vec<u8>, num: u32) {
assert_eq!(&data[..], &other[..]);
assert_eq!(data.len(), num as usize);
@@ -1099,6 +1111,34 @@ fn test_read_child_storage() {
assert_eq!(&v, &[0, 0, 0, 0]);
}
fn test_witness(proof: StorageProof, root: crate::Hash) {
use sp_externalities::Externalities;
let db: sp_trie::MemoryDB<crate::Hashing> = proof.into_memory_db();
let backend = sp_state_machine::TrieBackend::<_, crate::Hashing>::new(
db,
root,
);
let mut overlay = sp_state_machine::OverlayedChanges::default();
#[cfg(feature = "std")]
let mut offchain_overlay = Default::default();
let mut cache = sp_state_machine::StorageTransactionCache::<_, _, BlockNumber>::default();
let mut ext = sp_state_machine::Ext::new(
&mut overlay,
#[cfg(feature = "std")]
&mut offchain_overlay,
&mut cache,
&backend,
#[cfg(feature = "std")]
None,
#[cfg(feature = "std")]
None,
);
assert!(ext.storage(b"value3").is_some());
assert!(ext.storage_root().as_slice() == &root[..]);
ext.place_storage(vec![0], Some(vec![1]));
assert!(ext.storage_root().as_slice() != &root[..]);
}
#[cfg(test)]
mod tests {
use substrate_test_runtime_client::{
@@ -1157,4 +1197,33 @@ mod tests {
runtime_api.test_storage(&block_id).unwrap();
}
fn witness_backend() -> (sp_trie::MemoryDB<crate::Hashing>, crate::Hash) {
use sp_trie::TrieMut;
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);
trie.insert(b"value3", &[142]).expect("insert failed");
trie.insert(b"value4", &[124]).expect("insert failed");
};
(mdb, root)
}
#[test]
fn witness_backend_works() {
let (db, root) = witness_backend();
let backend = sp_state_machine::TrieBackend::<_, crate::Hashing>::new(
db,
root,
);
let proof = sp_state_machine::prove_read(backend, vec![b"value3"]).unwrap();
let client = TestClientBuilder::new()
.set_execution_strategy(ExecutionStrategy::Both)
.build();
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.chain_info().best_number);
runtime_api.test_witness(&block_id, proof, root).unwrap();
}
}