mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 19:21:13 +00:00
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:
Generated
+2
-1
@@ -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,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};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user