feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,64 @@
[package]
name = "pezsp-state-machine"
version = "0.35.0"
authors.workspace = true
description = "Bizinikiwi State Machine"
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
documentation = "https://docs.rs/pezsp-state-machine"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
arbitrary = { features = ["derive"], optional = true, workspace = true }
codec = { workspace = true }
hash-db = { workspace = true }
log = { workspace = true }
parking_lot = { optional = true, workspace = true, default-features = true }
rand = { optional = true, workspace = true, default-features = true }
smallvec = { workspace = true, default-features = true }
pezsp-core = { workspace = true }
pezsp-externalities = { workspace = true }
pezsp-panic-handler = { optional = true, workspace = true, default-features = true }
pezsp-trie = { workspace = true }
thiserror = { optional = true, workspace = true }
tracing = { optional = true, workspace = true, default-features = true }
trie-db = { workspace = true }
[dev-dependencies]
arbitrary = { features = ["derive"], workspace = true }
array-bytes = { workspace = true, default-features = true }
assert_matches = { workspace = true }
pretty_assertions = { workspace = true }
rand = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true, default-features = true }
[features]
default = ["std"]
fuzzing = ["arbitrary"]
std = [
"codec/std",
"hash-db/std",
"log/std",
"parking_lot",
"rand",
"pezsp-core/std",
"pezsp-externalities/std",
"pezsp-panic-handler",
"pezsp-runtime/std",
"pezsp-trie/std",
"thiserror",
"tracing",
"trie-db/std",
]
runtime-benchmarks = [
"pezsp-runtime/runtime-benchmarks",
"pezsp-trie/runtime-benchmarks",
]
@@ -0,0 +1,3 @@
Bizinikiwi state machine implementation.
License: Apache-2.0
@@ -0,0 +1,30 @@
[package]
name = "pezsp-state-machine-fuzz"
version = "0.0.0"
publish = false
license = "Apache-2.0"
edition = "2021"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
pezsp-runtime = { path = "../../runtime" }
[dependencies.pezsp-state-machine]
features = ["fuzzing"]
path = ".."
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[profile.release]
debug = 1
[[bin]]
name = "fuzz_append"
path = "fuzz_targets/fuzz_append.rs"
test = false
doc = false
@@ -0,0 +1,26 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
use libfuzzer_sys::fuzz_target;
use pezsp_state_machine::fuzzing::{fuzz_append, FuzzAppendPayload};
use pezsp_runtime::traits::BlakeTwo256;
fuzz_target!(|data: FuzzAppendPayload| {
fuzz_append::<BlakeTwo256>(data);
});
@@ -0,0 +1,443 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! State machine backends. These manage the code and storage of contracts.
#[cfg(feature = "std")]
use crate::trie_backend::TrieBackend;
use crate::{
trie_backend_essence::TrieBackendStorage, ChildStorageCollection, StorageCollection,
StorageKey, StorageValue, UsageInfo,
};
use alloc::vec::Vec;
use codec::Encode;
use core::marker::PhantomData;
use hash_db::Hasher;
use pezsp_core::storage::{ChildInfo, StateVersion, TrackedStorageKey};
#[cfg(feature = "std")]
use pezsp_core::traits::RuntimeCode;
use pezsp_trie::{MerkleValue, PrefixedMemoryDB, RandomState};
/// A struct containing arguments for iterating over the storage.
#[derive(Default)]
#[non_exhaustive]
pub struct IterArgs<'a> {
/// The prefix of the keys over which to iterate.
pub prefix: Option<&'a [u8]>,
/// The prefix from which to start the iteration from.
///
/// This is inclusive and the iteration will include the key which is specified here.
pub start_at: Option<&'a [u8]>,
/// If this is `true` then the iteration will *not* include
/// the key specified in `start_at`, if there is such a key.
pub start_at_exclusive: bool,
/// The info of the child trie over which to iterate over.
pub child_info: Option<ChildInfo>,
/// Whether to stop iteration when a missing trie node is reached.
///
/// When a missing trie node is reached the iterator will:
/// - return an error if this is set to `false` (default)
/// - return `None` if this is set to `true`
pub stop_on_incomplete_database: bool,
}
/// A trait for a raw storage iterator.
pub trait StorageIterator<H>
where
H: Hasher,
{
/// The state backend over which the iterator is iterating.
type Backend;
/// The error type.
type Error;
/// Fetches the next key from the storage.
fn next_key(
&mut self,
backend: &Self::Backend,
) -> Option<core::result::Result<StorageKey, Self::Error>>;
/// Fetches the next key and value from the storage.
fn next_pair(
&mut self,
backend: &Self::Backend,
) -> Option<core::result::Result<(StorageKey, StorageValue), Self::Error>>;
/// Returns whether the end of iteration was reached without an error.
fn was_complete(&self) -> bool;
}
/// An iterator over storage keys and values.
pub struct PairsIter<'a, H, I>
where
H: Hasher,
I: StorageIterator<H>,
{
backend: Option<&'a I::Backend>,
raw_iter: I,
_phantom: PhantomData<H>,
}
impl<'a, H, I> Iterator for PairsIter<'a, H, I>
where
H: Hasher,
I: StorageIterator<H>,
{
type Item = Result<(Vec<u8>, Vec<u8>), <I as StorageIterator<H>>::Error>;
fn next(&mut self) -> Option<Self::Item> {
self.raw_iter.next_pair(self.backend.as_ref()?)
}
}
impl<'a, H, I> Default for PairsIter<'a, H, I>
where
H: Hasher,
I: StorageIterator<H> + Default,
{
fn default() -> Self {
Self {
backend: Default::default(),
raw_iter: Default::default(),
_phantom: Default::default(),
}
}
}
impl<'a, H, I> PairsIter<'a, H, I>
where
H: Hasher,
I: StorageIterator<H> + Default,
{
#[cfg(feature = "std")]
pub(crate) fn was_complete(&self) -> bool {
self.raw_iter.was_complete()
}
}
/// An iterator over storage keys.
pub struct KeysIter<'a, H, I>
where
H: Hasher,
I: StorageIterator<H>,
{
backend: Option<&'a I::Backend>,
raw_iter: I,
_phantom: PhantomData<H>,
}
impl<'a, H, I> Iterator for KeysIter<'a, H, I>
where
H: Hasher,
I: StorageIterator<H>,
{
type Item = Result<Vec<u8>, <I as StorageIterator<H>>::Error>;
fn next(&mut self) -> Option<Self::Item> {
self.raw_iter.next_key(self.backend.as_ref()?)
}
}
impl<'a, H, I> Default for KeysIter<'a, H, I>
where
H: Hasher,
I: StorageIterator<H> + Default,
{
fn default() -> Self {
Self {
backend: Default::default(),
raw_iter: Default::default(),
_phantom: Default::default(),
}
}
}
/// The transaction type used by [`Backend`].
///
/// This transaction contains all the changes that need to be applied to the backend to create the
/// state for a new block.
pub type BackendTransaction<H> = PrefixedMemoryDB<H>;
/// 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>: core::fmt::Debug {
/// An error type when fetching data is not possible.
type Error: super::Error;
/// Type of trie backend storage.
type TrieBackendStorage: TrieBackendStorage<H>;
/// Type of the raw storage iterator.
type RawIter: StorageIterator<H, Backend = Self, Error = Self::Error>;
/// Get keyed storage or None if there is nothing associated.
fn storage(&self, key: &[u8]) -> Result<Option<StorageValue>, Self::Error>;
/// Get keyed storage value hash or None if there is nothing associated.
fn storage_hash(&self, key: &[u8]) -> Result<Option<H::Out>, Self::Error>;
/// Get the merkle value or None if there is nothing associated.
fn closest_merkle_value(&self, key: &[u8]) -> Result<Option<MerkleValue<H::Out>>, Self::Error>;
/// Get the child merkle value or None if there is nothing associated.
fn child_closest_merkle_value(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<MerkleValue<H::Out>>, Self::Error>;
/// Get child keyed child storage or None if there is nothing associated.
fn child_storage(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<StorageValue>, Self::Error>;
/// Get child keyed storage value hash or None if there is nothing associated.
fn child_storage_hash(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<H::Out>, Self::Error>;
/// true if a key exists in storage.
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
Ok(self.storage_hash(key)?.is_some())
}
/// true if a key exists in child storage.
fn exists_child_storage(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<bool, Self::Error> {
Ok(self.child_storage_hash(child_info, key)?.is_some())
}
/// Return the next key in storage in lexicographic order or `None` if there is no value.
fn next_storage_key(&self, key: &[u8]) -> Result<Option<StorageKey>, Self::Error>;
/// Return the next key in child storage in lexicographic order or `None` if there is no value.
fn next_child_storage_key(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<StorageKey>, Self::Error>;
/// Calculate the storage root, with given delta over what is already stored in
/// the backend, and produce a "transaction" that can be used to commit.
/// Does not include child storage updates.
fn storage_root<'a>(
&self,
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
state_version: StateVersion,
) -> (H::Out, BackendTransaction<H>)
where
H::Out: Ord;
/// Calculate the child storage root, with given delta over what is already stored in
/// the backend, and produce a "transaction" that can be used to commit. The second argument
/// is true if child storage root equals default storage root.
fn child_storage_root<'a>(
&self,
child_info: &ChildInfo,
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
state_version: StateVersion,
) -> (H::Out, bool, BackendTransaction<H>)
where
H::Out: Ord;
/// Returns a lifetimeless raw storage iterator.
fn raw_iter(&self, args: IterArgs) -> Result<Self::RawIter, Self::Error>;
/// Get an iterator over key/value pairs.
fn pairs<'a>(&'a self, args: IterArgs) -> Result<PairsIter<'a, H, Self::RawIter>, Self::Error> {
Ok(PairsIter {
backend: Some(self),
raw_iter: self.raw_iter(args)?,
_phantom: Default::default(),
})
}
/// Get an iterator over keys.
fn keys<'a>(&'a self, args: IterArgs) -> Result<KeysIter<'a, H, Self::RawIter>, Self::Error> {
Ok(KeysIter {
backend: Some(self),
raw_iter: self.raw_iter(args)?,
_phantom: Default::default(),
})
}
/// Calculate the storage root, with given delta over what is already stored
/// in the backend, and produce a "transaction" that can be used to commit.
/// Does include child storage updates.
fn full_storage_root<'a>(
&self,
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
child_deltas: impl Iterator<
Item = (&'a ChildInfo, impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>),
>,
state_version: StateVersion,
) -> (H::Out, BackendTransaction<H>)
where
H::Out: Ord + Encode,
{
let mut txs = BackendTransaction::with_hasher(RandomState::default());
let mut child_roots: Vec<_> = Default::default();
// child first
for (child_info, child_delta) in child_deltas {
let (child_root, empty, child_txs) =
self.child_storage_root(child_info, child_delta, state_version);
let prefixed_storage_key = child_info.prefixed_storage_key();
txs.consolidate(child_txs);
if empty {
child_roots.push((prefixed_storage_key.into_inner(), None));
} else {
child_roots.push((prefixed_storage_key.into_inner(), Some(child_root.encode())));
}
}
let (root, parent_txs) = self.storage_root(
delta
.map(|(k, v)| (k, v.as_ref().map(|v| &v[..])))
.chain(child_roots.iter().map(|(k, v)| (&k[..], v.as_ref().map(|v| &v[..])))),
state_version,
);
txs.consolidate(parent_txs);
(root, txs)
}
/// Register stats from overlay of state machine.
///
/// By default nothing is registered.
fn register_overlay_stats(&self, _stats: &crate::stats::StateMachineStats);
/// Query backend usage statistics (i/o, memory)
///
/// Not all implementations are expected to be able to do this. In the
/// case when they don't, empty statistics is returned.
fn usage_info(&self) -> UsageInfo;
/// Wipe the state database.
fn wipe(&self) -> Result<(), Self::Error> {
unimplemented!()
}
/// Commit given transaction to storage.
fn commit(
&self,
_: H::Out,
_: BackendTransaction<H>,
_: StorageCollection,
_: ChildStorageCollection,
) -> Result<(), Self::Error> {
unimplemented!()
}
/// Get the read/write count of the db
fn read_write_count(&self) -> (u32, u32, u32, u32) {
unimplemented!()
}
/// Get the read/write count of the db
fn reset_read_write_count(&self) {
unimplemented!()
}
/// Get the whitelist for tracking db reads/writes
fn get_whitelist(&self) -> Vec<TrackedStorageKey> {
Default::default()
}
/// Update the whitelist for tracking db reads/writes
fn set_whitelist(&self, _: Vec<TrackedStorageKey>) {}
/// Estimate proof size
fn proof_size(&self) -> Option<u32> {
unimplemented!()
}
/// Extend storage info for benchmarking db
fn get_read_and_written_keys(&self) -> Vec<(Vec<u8>, u32, u32, bool)> {
unimplemented!()
}
}
/// Something that can be converted into a [`TrieBackend`].
#[cfg(feature = "std")]
pub trait AsTrieBackend<H: Hasher, C = pezsp_trie::cache::LocalTrieCache<H>> {
/// Type of trie backend storage.
type TrieBackendStorage: TrieBackendStorage<H>;
/// Return the type as [`TrieBackend`].
fn as_trie_backend(&self) -> &TrieBackend<Self::TrieBackendStorage, H, C>;
}
/// Wrapper to create a [`RuntimeCode`] from a type that implements [`Backend`].
#[cfg(feature = "std")]
pub struct BackendRuntimeCode<'a, B, H> {
backend: &'a B,
_marker: PhantomData<H>,
}
#[cfg(feature = "std")]
impl<'a, B: Backend<H>, H: Hasher> pezsp_core::traits::FetchRuntimeCode
for BackendRuntimeCode<'a, B, H>
{
fn fetch_runtime_code(&self) -> Option<std::borrow::Cow<'_, [u8]>> {
self.backend
.storage(pezsp_core::storage::well_known_keys::CODE)
.ok()
.flatten()
.map(Into::into)
}
}
#[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 {
Self { backend, _marker: PhantomData }
}
/// Return the [`RuntimeCode`] build from the wrapped `backend`.
pub fn runtime_code(&self) -> Result<RuntimeCode<'_>, &'static str> {
let hash = self
.backend
.storage_hash(pezsp_core::storage::well_known_keys::CODE)
.ok()
.flatten()
.ok_or("`:code` hash not found")?
.encode();
let heap_pages = self
.backend
.storage(pezsp_core::storage::well_known_keys::HEAP_PAGES)
.ok()
.flatten()
.and_then(|d| codec::Decode::decode(&mut &d[..]).ok());
Ok(RuntimeCode { code_fetcher: self, hash, heap_pages })
}
}
@@ -0,0 +1,461 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Basic implementation for Externalities.
use crate::{Backend, OverlayedChanges, StorageKey, StorageValue};
use alloc::{boxed::Box, collections::BTreeMap, vec::Vec};
use codec::Encode;
use core::{
any::{Any, TypeId},
iter::FromIterator,
};
use hash_db::Hasher;
use log::warn;
use pezsp_core::{
storage::{
well_known_keys::is_child_storage_key, ChildInfo, StateVersion, Storage, TrackedStorageKey,
},
traits::Externalities,
Blake2Hasher,
};
use pezsp_externalities::{Extension, Extensions, MultiRemovalResults};
use pezsp_trie::{empty_child_trie_root, LayoutV0, LayoutV1, TrieConfiguration};
/// Simple Map-based Externalities impl.
#[derive(Debug)]
pub struct BasicExternalities {
overlay: OverlayedChanges<Blake2Hasher>,
extensions: Extensions,
}
impl BasicExternalities {
/// Create a new instance of `BasicExternalities`
pub fn new(inner: Storage) -> Self {
BasicExternalities { overlay: inner.into(), extensions: Default::default() }
}
/// New basic externalities with empty storage.
pub fn new_empty() -> Self {
Self::new(Storage::default())
}
/// Insert key/value
pub fn insert(&mut self, k: StorageKey, v: StorageValue) {
self.overlay.set_storage(k, Some(v));
}
/// Consume self and returns inner storages
#[cfg(feature = "std")]
pub fn into_storages(mut self) -> Storage {
Storage {
top: self
.overlay
.changes_mut()
.filter_map(|(k, v)| v.value().map(|v| (k.to_vec(), v.to_vec())))
.collect(),
children_default: self
.overlay
.children_mut()
.map(|(iter, i)| {
(
i.storage_key().to_vec(),
pezsp_core::storage::StorageChild {
data: iter
.filter_map(|(k, v)| v.value().map(|v| (k.to_vec(), v.to_vec())))
.collect(),
child_info: i.clone(),
},
)
})
.collect(),
}
}
/// Execute the given closure `f` with the externalities set and initialized with `storage`.
///
/// Returns the result of the closure and updates `storage` with all changes.
#[cfg(feature = "std")]
pub fn execute_with_storage<R>(
storage: &mut pezsp_core::storage::Storage,
f: impl FnOnce() -> R,
) -> R {
let mut ext = Self::new(core::mem::take(storage));
let r = ext.execute_with(f);
*storage = ext.into_storages();
r
}
/// Execute the given closure while `self` is set as externalities.
///
/// Returns the result of the given closure.
pub fn execute_with<R>(&mut self, f: impl FnOnce() -> R) -> R {
pezsp_externalities::set_and_run_with_externalities(self, f)
}
/// List of active extensions.
pub fn extensions(&mut self) -> &mut Extensions {
&mut self.extensions
}
/// Register an extension.
pub fn register_extension(&mut self, ext: impl Extension) {
self.extensions.register(ext);
}
}
#[cfg(test)]
impl PartialEq for BasicExternalities {
fn eq(&self, other: &Self) -> bool {
self.overlay
.changes()
.map(|(k, v)| (k, v.value_ref().materialize()))
.collect::<BTreeMap<_, _>>() ==
other
.overlay
.changes()
.map(|(k, v)| (k, v.value_ref().materialize()))
.collect::<BTreeMap<_, _>>() &&
self.overlay
.children()
.map(|(iter, i)| {
(
i,
iter.map(|(k, v)| (k, v.value_ref().materialize()))
.collect::<BTreeMap<_, _>>(),
)
})
.collect::<BTreeMap<_, _>>() ==
other
.overlay
.children()
.map(|(iter, i)| {
(
i,
iter.map(|(k, v)| (k, v.value_ref().materialize()))
.collect::<BTreeMap<_, _>>(),
)
})
.collect::<BTreeMap<_, _>>()
}
}
impl FromIterator<(StorageKey, StorageValue)> for BasicExternalities {
fn from_iter<I: IntoIterator<Item = (StorageKey, StorageValue)>>(iter: I) -> Self {
let mut t = Self::default();
iter.into_iter().for_each(|(k, v)| t.insert(k, v));
t
}
}
impl Default for BasicExternalities {
fn default() -> Self {
Self::new(Default::default())
}
}
impl From<BTreeMap<StorageKey, StorageValue>> for BasicExternalities {
fn from(map: BTreeMap<StorageKey, StorageValue>) -> Self {
Self::from_iter(map.into_iter())
}
}
impl Externalities for BasicExternalities {
fn set_offchain_storage(&mut self, _key: &[u8], _value: Option<&[u8]>) {}
fn storage(&mut self, key: &[u8]) -> Option<StorageValue> {
self.overlay.storage(key).and_then(|v| v.map(|v| v.to_vec()))
}
fn storage_hash(&mut self, key: &[u8]) -> Option<Vec<u8>> {
self.storage(key).map(|v| Blake2Hasher::hash(&v).encode())
}
fn child_storage(&mut self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageValue> {
self.overlay.child_storage(child_info, key).and_then(|v| v.map(|v| v.to_vec()))
}
fn child_storage_hash(&mut self, child_info: &ChildInfo, key: &[u8]) -> Option<Vec<u8>> {
self.child_storage(child_info, key).map(|v| Blake2Hasher::hash(&v).encode())
}
fn next_storage_key(&mut self, key: &[u8]) -> Option<StorageKey> {
self.overlay.iter_after(key).find_map(|(k, v)| v.value().map(|_| k.to_vec()))
}
fn next_child_storage_key(&mut self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageKey> {
self.overlay
.child_iter_after(child_info.storage_key(), key)
.find_map(|(k, v)| v.value().map(|_| k.to_vec()))
}
fn place_storage(&mut self, key: StorageKey, maybe_value: Option<StorageValue>) {
if is_child_storage_key(&key) {
warn!(target: "trie", "Refuse to set child storage key via main storage");
return;
}
self.overlay.set_storage(key, maybe_value)
}
fn place_child_storage(
&mut self,
child_info: &ChildInfo,
key: StorageKey,
value: Option<StorageValue>,
) {
self.overlay.set_child_storage(child_info, key, value);
}
fn kill_child_storage(
&mut self,
child_info: &ChildInfo,
_maybe_limit: Option<u32>,
_maybe_cursor: Option<&[u8]>,
) -> MultiRemovalResults {
let count = self.overlay.clear_child_storage(child_info);
MultiRemovalResults { maybe_cursor: None, backend: count, unique: count, loops: count }
}
fn clear_prefix(
&mut self,
prefix: &[u8],
_maybe_limit: Option<u32>,
_maybe_cursor: Option<&[u8]>,
) -> MultiRemovalResults {
if is_child_storage_key(prefix) {
warn!(
target: "trie",
"Refuse to clear prefix that is part of child storage key via main storage"
);
let maybe_cursor = Some(prefix.to_vec());
return MultiRemovalResults { maybe_cursor, backend: 0, unique: 0, loops: 0 };
}
let count = self.overlay.clear_prefix(prefix);
MultiRemovalResults { maybe_cursor: None, backend: count, unique: count, loops: count }
}
fn clear_child_prefix(
&mut self,
child_info: &ChildInfo,
prefix: &[u8],
_maybe_limit: Option<u32>,
_maybe_cursor: Option<&[u8]>,
) -> MultiRemovalResults {
let count = self.overlay.clear_child_prefix(child_info, prefix);
MultiRemovalResults { maybe_cursor: None, backend: count, unique: count, loops: count }
}
fn storage_append(&mut self, key: Vec<u8>, element: Vec<u8>) {
self.overlay.append_storage(key, element, Default::default);
}
fn storage_root(&mut self, state_version: StateVersion) -> Vec<u8> {
let mut top = self
.overlay
.changes_mut()
.filter_map(|(k, v)| v.value().map(|v| (k.clone(), v.clone())))
.collect::<BTreeMap<_, _>>();
// Single child trie implementation currently allows using the same child
// empty root for all child trie. Using null storage key until multiple
// type of child trie support.
let empty_hash = empty_child_trie_root::<LayoutV1<Blake2Hasher>>();
for child_info in self.overlay.children().map(|d| d.1.clone()).collect::<Vec<_>>() {
let child_root = self.child_storage_root(&child_info, state_version);
if empty_hash[..] == child_root[..] {
top.remove(child_info.prefixed_storage_key().as_slice());
} else {
top.insert(child_info.prefixed_storage_key().into_inner(), child_root);
}
}
match state_version {
StateVersion::V0 => LayoutV0::<Blake2Hasher>::trie_root(top).as_ref().into(),
StateVersion::V1 => LayoutV1::<Blake2Hasher>::trie_root(top).as_ref().into(),
}
}
fn child_storage_root(
&mut self,
child_info: &ChildInfo,
state_version: StateVersion,
) -> Vec<u8> {
if let Some((data, child_info)) = self.overlay.child_changes_mut(child_info.storage_key()) {
let delta =
data.into_iter().map(|(k, v)| (k.as_ref(), v.value().map(|v| v.as_slice())));
crate::in_memory_backend::new_in_mem::<Blake2Hasher>()
.child_storage_root(&child_info, delta, state_version)
.0
} else {
empty_child_trie_root::<LayoutV1<Blake2Hasher>>()
}
.encode()
}
fn storage_start_transaction(&mut self) {
self.overlay.start_transaction()
}
fn storage_rollback_transaction(&mut self) -> Result<(), ()> {
self.overlay.rollback_transaction().map_err(drop)
}
fn storage_commit_transaction(&mut self) -> Result<(), ()> {
self.overlay.commit_transaction().map_err(drop)
}
fn wipe(&mut self) {}
fn commit(&mut self) {}
fn read_write_count(&self) -> (u32, u32, u32, u32) {
unimplemented!("read_write_count is not supported in Basic")
}
fn reset_read_write_count(&mut self) {
unimplemented!("reset_read_write_count is not supported in Basic")
}
fn get_whitelist(&self) -> Vec<TrackedStorageKey> {
unimplemented!("get_whitelist is not supported in Basic")
}
fn set_whitelist(&mut self, _: Vec<TrackedStorageKey>) {
unimplemented!("set_whitelist is not supported in Basic")
}
fn get_read_and_written_keys(&self) -> Vec<(Vec<u8>, u32, u32, bool)> {
unimplemented!("get_read_and_written_keys is not supported in Basic")
}
}
impl pezsp_externalities::ExtensionStore for BasicExternalities {
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
self.extensions.get_mut(type_id)
}
fn register_extension_with_type_id(
&mut self,
type_id: TypeId,
extension: Box<dyn pezsp_externalities::Extension>,
) -> Result<(), pezsp_externalities::Error> {
self.extensions.register_with_type_id(type_id, extension)
}
fn deregister_extension_by_type_id(
&mut self,
type_id: TypeId,
) -> Result<(), pezsp_externalities::Error> {
if self.extensions.deregister(type_id) {
Ok(())
} else {
Err(pezsp_externalities::Error::ExtensionIsNotRegistered(type_id))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pezsp_core::{
map,
storage::{well_known_keys::CODE, Storage, StorageChild},
};
#[test]
fn commit_should_work() {
let mut ext = BasicExternalities::default();
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
ext.set_storage(b"dog".to_vec(), b"puppy".to_vec());
ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec());
let root = array_bytes::hex2bytes_unchecked(
"39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa",
);
assert_eq!(&ext.storage_root(StateVersion::default())[..], &root);
}
#[test]
fn set_and_retrieve_code() {
let mut ext = BasicExternalities::default();
let code = vec![1, 2, 3];
ext.set_storage(CODE.to_vec(), code.clone());
assert_eq!(&ext.storage(CODE).unwrap(), &code);
}
#[test]
fn children_works() {
let child_info = ChildInfo::new_default(b"storage_key");
let child_info = &child_info;
let mut ext = BasicExternalities::new(Storage {
top: Default::default(),
children_default: map![
child_info.storage_key().to_vec() => StorageChild {
data: map![ b"doe".to_vec() => b"reindeer".to_vec() ],
child_info: child_info.to_owned(),
}
],
});
assert_eq!(ext.child_storage(child_info, b"doe"), Some(b"reindeer".to_vec()));
ext.set_child_storage(child_info, b"dog".to_vec(), b"puppy".to_vec());
assert_eq!(ext.child_storage(child_info, b"dog"), Some(b"puppy".to_vec()));
ext.clear_child_storage(child_info, b"dog");
assert_eq!(ext.child_storage(child_info, b"dog"), None);
let _ = ext.kill_child_storage(child_info, None, None);
assert_eq!(ext.child_storage(child_info, b"doe"), None);
}
#[test]
fn kill_child_storage_returns_num_elements_removed() {
let child_info = ChildInfo::new_default(b"storage_key");
let child_info = &child_info;
let mut ext = BasicExternalities::new(Storage {
top: Default::default(),
children_default: map![
child_info.storage_key().to_vec() => StorageChild {
data: map![
b"doe".to_vec() => b"reindeer".to_vec(),
b"dog".to_vec() => b"puppy".to_vec(),
b"hello".to_vec() => b"world".to_vec(),
],
child_info: child_info.to_owned(),
}
],
});
let res = ext.kill_child_storage(child_info, None, None);
assert_eq!(res.deconstruct(), (None, 3, 3, 3));
}
#[test]
fn basic_externalities_is_empty() {
// Make sure no values are set by default in `BasicExternalities`.
let storage = BasicExternalities::new_empty().into_storages();
assert!(storage.top.is_empty());
assert!(storage.children_default.is_empty());
}
}
@@ -0,0 +1,48 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// State Machine Errors
use core::fmt;
/// State Machine Error bound.
///
/// This should reflect Wasm error type bound for future compatibility.
pub trait Error: 'static + fmt::Debug + fmt::Display + Send + Sync {}
impl<T: 'static + fmt::Debug + fmt::Display + Send + Sync> Error for T {}
/// Externalities Error.
///
/// Externalities are not really allowed to have errors, since it's assumed that dependent code
/// would not be executed unless externalities were available. This is included for completeness,
/// and as a transition away from the pre-existing framework.
#[derive(Debug, Eq, PartialEq)]
#[allow(missing_docs)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum ExecutionError {
#[cfg_attr(feature = "std", error("Backend error {0:?}"))]
Backend(crate::DefaultError),
#[cfg_attr(feature = "std", error("`:code` entry does not exist in storage"))]
CodeEntryDoesNotExist,
#[cfg_attr(feature = "std", error("Unable to generate proof"))]
UnableToGenerateProof,
#[cfg_attr(feature = "std", error("Invalid execution proof"))]
InvalidProof,
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,319 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! State machine fuzzing implementation, behind `fuzzing` feature.
use super::{ext::Ext, *};
use crate::ext::StorageAppend;
use arbitrary::Arbitrary;
#[cfg(test)]
use codec::Encode;
use hash_db::Hasher;
use pezsp_core::{storage::StateVersion, traits::Externalities};
#[cfg(test)]
use pezsp_runtime::traits::BlakeTwo256;
use pezsp_trie::PrefixedMemoryDB;
use std::collections::BTreeMap;
#[derive(Arbitrary, Debug, Clone)]
enum DataLength {
Zero = 0,
Small = 1,
Medium = 3,
Big = 300, // 2 byte scale encode length
}
#[derive(Arbitrary, Debug, Clone)]
#[repr(u8)]
enum DataValue {
A = b'a',
B = b'b',
C = b'c',
D = b'd', // This can be read as a multiple byte compact length.
EasyBug = 20u8, // value compact len.
}
/// Action to fuzz
#[derive(Arbitrary, Debug, Clone)]
enum FuzzAppendItem {
Append(DataValue, DataLength),
Insert(DataValue, DataLength),
StartTransaction,
RollbackTransaction,
CommitTransaction,
Read,
Remove,
// To go over 256 items easily (different compact size then).
Append50(DataValue, DataLength),
}
/// Arbitrary payload for fuzzing append.
#[derive(Arbitrary, Debug, Clone)]
pub struct FuzzAppendPayload(Vec<FuzzAppendItem>, Option<(DataValue, DataLength)>);
struct SimpleOverlay {
data: Vec<BTreeMap<Vec<u8>, Option<Vec<u8>>>>,
}
impl Default for SimpleOverlay {
fn default() -> Self {
Self { data: vec![BTreeMap::new()] }
}
}
impl SimpleOverlay {
fn insert(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) {
self.data.last_mut().expect("always at least one item").insert(key, value);
}
fn append<H>(
&mut self,
key: Vec<u8>,
value: Vec<u8>,
backend: &mut TrieBackend<PrefixedMemoryDB<H>, H>,
) where
H: Hasher,
H::Out: codec::Decode + codec::Encode + 'static,
{
let current_value = self
.data
.last_mut()
.expect("always at least one item")
.entry(key.clone())
.or_insert_with(|| {
Some(backend.storage(&key).expect("Ext not allowed to fail").unwrap_or_default())
});
if current_value.is_none() {
*current_value = Some(vec![]);
}
StorageAppend::new(current_value.as_mut().expect("init above")).append(value);
}
fn get(&mut self, key: &[u8]) -> Option<&Vec<u8>> {
self.data
.last_mut()
.expect("always at least one item")
.get(key)
.and_then(|o| o.as_ref())
}
fn commit_transaction(&mut self) {
if let Some(to_commit) = self.data.pop() {
let dest = self.data.last_mut().expect("always at least one item");
for (k, v) in to_commit.into_iter() {
dest.insert(k, v);
}
}
}
fn rollback_transaction(&mut self) {
let _ = self.data.pop();
}
fn start_transaction(&mut self) {
let cloned = self.data.last().expect("always at least one item").clone();
self.data.push(cloned);
}
}
struct FuzzAppendState<H: Hasher> {
key: Vec<u8>,
// reference simple implementation
reference: SimpleOverlay,
// trie backend
backend: TrieBackend<PrefixedMemoryDB<H>, H>,
// Standard Overlay
overlay: OverlayedChanges<H>,
// block dropping/commiting too many transaction
transaction_depth: usize,
}
impl<H> FuzzAppendState<H>
where
H: Hasher,
H::Out: codec::Decode + codec::Encode + 'static,
{
fn process_item(&mut self, item: FuzzAppendItem) {
let mut ext = Ext::new(&mut self.overlay, &mut self.backend, None);
match item {
FuzzAppendItem::Append(value, length) => {
let value = vec![value as u8; length as usize];
ext.storage_append(self.key.clone(), value.clone());
self.reference.append(self.key.clone(), value, &mut self.backend);
},
FuzzAppendItem::Append50(value, length) => {
let value = vec![value as u8; length as usize];
for _ in 0..50 {
let mut ext = Ext::new(&mut self.overlay, &mut self.backend, None);
ext.storage_append(self.key.clone(), value.clone());
self.reference.append(self.key.clone(), value.clone(), &mut self.backend);
}
},
FuzzAppendItem::Insert(value, length) => {
let value = vec![value as u8; length as usize];
ext.set_storage(self.key.clone(), value.clone());
self.reference.insert(self.key.clone(), Some(value));
},
FuzzAppendItem::Remove => {
ext.clear_storage(&self.key);
self.reference.insert(self.key.clone(), None);
},
FuzzAppendItem::Read => {
let left = ext.storage(self.key.as_slice());
let right = self.reference.get(self.key.as_slice());
assert_eq!(left.as_ref(), right);
},
FuzzAppendItem::StartTransaction => {
self.transaction_depth += 1;
self.reference.start_transaction();
ext.storage_start_transaction();
},
FuzzAppendItem::RollbackTransaction => {
if self.transaction_depth == 0 {
return;
}
self.transaction_depth -= 1;
self.reference.rollback_transaction();
ext.storage_rollback_transaction().unwrap();
},
FuzzAppendItem::CommitTransaction => {
if self.transaction_depth == 0 {
return;
}
self.transaction_depth -= 1;
self.reference.commit_transaction();
ext.storage_commit_transaction().unwrap();
},
}
}
fn check_final_state(&mut self) {
let mut ext = Ext::new(&mut self.overlay, &mut self.backend, None);
let left = ext.storage(self.key.as_slice());
let right = self.reference.get(self.key.as_slice());
assert_eq!(left.as_ref(), right);
}
}
#[test]
fn fuzz_scenarii() {
assert_eq!(codec::Compact(5u16).encode()[0], DataValue::EasyBug as u8);
let scenarii = vec![
(
vec![
FuzzAppendItem::Append(DataValue::A, DataLength::Small),
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Append50(DataValue::D, DataLength::Small),
FuzzAppendItem::Read,
FuzzAppendItem::RollbackTransaction,
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Append(DataValue::D, DataLength::Small),
FuzzAppendItem::Read,
FuzzAppendItem::RollbackTransaction,
],
Some((DataValue::D, DataLength::Small)),
),
(
vec![
FuzzAppendItem::Append(DataValue::B, DataLength::Small),
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Append(DataValue::A, DataLength::Small),
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Remove,
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Append(DataValue::A, DataLength::Zero),
FuzzAppendItem::CommitTransaction,
FuzzAppendItem::CommitTransaction,
FuzzAppendItem::Remove,
],
Some((DataValue::EasyBug, DataLength::Small)),
),
(
vec![
FuzzAppendItem::Append(DataValue::A, DataLength::Small),
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Append(DataValue::A, DataLength::Medium),
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Remove,
FuzzAppendItem::CommitTransaction,
FuzzAppendItem::RollbackTransaction,
],
Some((DataValue::B, DataLength::Big)),
),
(
vec![
FuzzAppendItem::Append(DataValue::A, DataLength::Big),
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Append(DataValue::A, DataLength::Medium),
FuzzAppendItem::Remove,
FuzzAppendItem::RollbackTransaction,
FuzzAppendItem::StartTransaction,
FuzzAppendItem::Append(DataValue::A, DataLength::Zero),
],
None,
),
(
vec![
FuzzAppendItem::StartTransaction,
FuzzAppendItem::RollbackTransaction,
FuzzAppendItem::RollbackTransaction,
FuzzAppendItem::Append(DataValue::A, DataLength::Zero),
],
None,
),
(vec![FuzzAppendItem::StartTransaction], Some((DataValue::EasyBug, DataLength::Zero))),
];
for (scenario, init) in scenarii.into_iter() {
fuzz_append::<BlakeTwo256>(FuzzAppendPayload(scenario, init));
}
}
/// Test append operation for a given fuzzing payload.
pub fn fuzz_append<H>(payload: FuzzAppendPayload)
where
H: Hasher,
H::Out: codec::Decode + codec::Encode + 'static,
{
let FuzzAppendPayload(to_fuzz, initial) = payload;
let key = b"k".to_vec();
let mut reference = SimpleOverlay::default();
let initial: BTreeMap<_, _> = initial
.into_iter()
.map(|(v, l)| (key.clone(), vec![v as u8; l as usize]))
.collect();
for (k, v) in initial.iter() {
reference.data[0].insert(k.clone(), Some(v.clone()));
}
reference.start_transaction(); // level 0 is backend, keep it untouched.
let overlay = OverlayedChanges::default();
let mut state = FuzzAppendState::<H> {
key,
reference,
overlay,
backend: (initial, StateVersion::default()).into(),
transaction_depth: 0,
};
for item in to_fuzz {
state.process_item(item);
}
state.check_final_state();
}
@@ -0,0 +1,237 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! State machine in memory backend.
use crate::{
backend::Backend, trie_backend::TrieBackend, StorageCollection, StorageKey, StorageValue,
TrieBackendBuilder,
};
use alloc::{collections::BTreeMap, vec::Vec};
use codec::Codec;
use hash_db::Hasher;
use pezsp_core::storage::{ChildInfo, StateVersion, Storage};
use pezsp_trie::{empty_trie_root, LayoutV1, PrefixedMemoryDB, RandomState};
#[cfg(feature = "std")]
use std::collections::HashMap as MapType;
#[cfg(not(feature = "std"))]
use alloc::collections::BTreeMap as MapType;
/// Create a new empty instance of in-memory backend.
pub fn new_in_mem<H>() -> TrieBackend<PrefixedMemoryDB<H>, H>
where
H: Hasher,
H::Out: Codec + Ord,
{
// V1 is same as V0 for an empty trie.
TrieBackendBuilder::new(
PrefixedMemoryDB::with_hasher(RandomState::default()),
empty_trie_root::<LayoutV1<H>>(),
)
.build()
}
impl<H: Hasher> TrieBackend<PrefixedMemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
/// Copy the state, with applied updates
pub fn update<T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>>(
&self,
changes: T,
state_version: StateVersion,
) -> Self {
let mut clone = self.clone();
clone.insert(changes, state_version);
clone
}
/// Insert values into backend trie.
pub fn insert<T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>>(
&mut self,
changes: T,
state_version: StateVersion,
) {
let (top, child) = changes.into_iter().partition::<Vec<_>, _>(|v| v.0.is_none());
let (root, transaction) = self.full_storage_root(
top.iter().flat_map(|(_, v)| v).map(|(k, v)| (&k[..], v.as_deref())),
child.iter().filter_map(|v| {
v.0.as_ref().map(|c| (c, v.1.iter().map(|(k, v)| (&k[..], v.as_deref()))))
}),
state_version,
);
self.apply_transaction(root, transaction);
}
/// Merge trie nodes into this backend.
pub fn update_backend(&self, root: H::Out, changes: PrefixedMemoryDB<H>) -> Self {
let mut clone = self.backend_storage().clone();
clone.consolidate(changes);
TrieBackendBuilder::new(clone, root).build()
}
/// Apply the given transaction to this backend and set the root to the given value.
pub fn apply_transaction(&mut self, root: H::Out, transaction: PrefixedMemoryDB<H>) {
let mut storage = core::mem::take(self).into_storage();
storage.consolidate(transaction);
*self = TrieBackendBuilder::new(storage, root).build();
}
/// Compare with another in-memory backend.
pub fn eq(&self, other: &Self) -> bool {
self.root() == other.root()
}
}
impl<H: Hasher> Clone for TrieBackend<PrefixedMemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn clone(&self) -> Self {
TrieBackendBuilder::new(self.backend_storage().clone(), *self.root()).build()
}
}
impl<H> Default for TrieBackend<PrefixedMemoryDB<H>, H>
where
H: Hasher,
H::Out: Codec + Ord,
{
fn default() -> Self {
new_in_mem()
}
}
impl<H: Hasher> From<(MapType<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>, StateVersion)>
for TrieBackend<PrefixedMemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn from(
(inner, state_version): (
MapType<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>,
StateVersion,
),
) -> Self {
let mut backend = new_in_mem();
backend.insert(
inner
.into_iter()
.map(|(k, m)| (k, m.into_iter().map(|(k, v)| (k, Some(v))).collect())),
state_version,
);
backend
}
}
#[cfg(feature = "std")]
impl<H: Hasher> From<(Storage, StateVersion)> for TrieBackend<PrefixedMemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn from((inners, state_version): (Storage, StateVersion)) -> Self {
let mut inner: MapType<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>> = inners
.children_default
.into_values()
.map(|c| (Some(c.child_info), c.data))
.collect();
inner.insert(None, inners.top);
(inner, state_version).into()
}
}
impl<H: Hasher> From<(BTreeMap<StorageKey, StorageValue>, StateVersion)>
for TrieBackend<PrefixedMemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn from((inner, state_version): (BTreeMap<StorageKey, StorageValue>, StateVersion)) -> Self {
let mut expanded = MapType::new();
expanded.insert(None, inner);
(expanded, state_version).into()
}
}
impl<H: Hasher> From<(Vec<(Option<ChildInfo>, StorageCollection)>, StateVersion)>
for TrieBackend<PrefixedMemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn from(
(inner, state_version): (Vec<(Option<ChildInfo>, StorageCollection)>, StateVersion),
) -> Self {
let mut expanded: MapType<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>> =
MapType::new();
for (child_info, key_values) in inner {
let entry = expanded.entry(child_info).or_default();
for (key, value) in key_values {
if let Some(value) = value {
entry.insert(key, value);
}
}
}
(expanded, state_version).into()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::{AsTrieBackend, Backend};
use pezsp_core::storage::StateVersion;
use pezsp_runtime::traits::BlakeTwo256;
/// Assert in memory backend with only child trie keys works as trie backend.
#[test]
fn in_memory_with_child_trie_only() {
let state_version = StateVersion::default();
let storage = new_in_mem::<BlakeTwo256>();
let child_info = ChildInfo::new_default(b"1");
let child_info = &child_info;
let storage = storage.update(
vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])],
state_version,
);
let trie_backend = storage.as_trie_backend();
assert_eq!(trie_backend.child_storage(child_info, b"2").unwrap(), Some(b"3".to_vec()));
let storage_key = child_info.prefixed_storage_key();
assert!(trie_backend.storage(storage_key.as_slice()).unwrap().is_some());
}
#[test]
fn insert_multiple_times_child_data_works() {
let state_version = StateVersion::default();
let mut storage = new_in_mem::<BlakeTwo256>();
let child_info = ChildInfo::new_default(b"1");
storage.insert(
vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])],
state_version,
);
storage.insert(
vec![(Some(child_info.clone()), vec![(b"1".to_vec(), Some(b"3".to_vec()))])],
state_version,
);
assert_eq!(storage.child_storage(&child_info, &b"2"[..]), Ok(Some(b"3".to_vec())));
assert_eq!(storage.child_storage(&child_info, &b"1"[..]), Ok(Some(b"3".to_vec())));
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,133 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Overlayed changes for offchain indexing.
use super::changeset::OverlayedMap;
use alloc::vec::Vec;
use pezsp_core::offchain::OffchainOverlayedChange;
/// In-memory storage for offchain workers recoding changes for the actual offchain storage
/// implementation.
#[derive(Debug, Clone, Default)]
pub struct OffchainOverlayedChanges(OverlayedMap<(Vec<u8>, Vec<u8>), OffchainOverlayedChange>);
/// Item for iterating over offchain changes.
///
/// First element i a tuple of `(prefix, key)`, second element ist the actual change
/// (remove or set value).
type OffchainOverlayedChangesItem<'i> = (&'i (Vec<u8>, Vec<u8>), &'i OffchainOverlayedChange);
/// Iterator over offchain changes, owned memory version.
type OffchainOverlayedChangesItemOwned = ((Vec<u8>, Vec<u8>), OffchainOverlayedChange);
impl OffchainOverlayedChanges {
/// Consume the offchain storage and iterate over all key value pairs.
pub fn into_iter(self) -> impl Iterator<Item = OffchainOverlayedChangesItemOwned> {
self.0.into_changes().map(|kv| (kv.0, kv.1.into_value()))
}
/// Iterate over all key value pairs by reference.
pub fn iter(&mut self) -> impl Iterator<Item = OffchainOverlayedChangesItem<'_>> {
self.0.changes().map(|kv| (kv.0, kv.1.value_ref()))
}
/// Drain all elements of changeset.
pub fn drain(&mut self) -> impl Iterator<Item = OffchainOverlayedChangesItemOwned> {
core::mem::take(self).into_iter()
}
/// Remove a key and its associated value from the offchain database.
pub fn remove(&mut self, prefix: &[u8], key: &[u8]) {
let _ = self.0.set_offchain(
(prefix.to_vec(), key.to_vec()),
OffchainOverlayedChange::Remove,
None,
);
}
/// Set the value associated with a key under a prefix to the value provided.
pub fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) {
let _ = self.0.set_offchain(
(prefix.to_vec(), key.to_vec()),
OffchainOverlayedChange::SetValue(value.to_vec()),
None,
);
}
/// Obtain a associated value to the given key in storage with prefix.
pub fn get(&mut self, prefix: &[u8], key: &[u8]) -> Option<OffchainOverlayedChange> {
let key = (prefix.to_vec(), key.to_vec());
self.0.get(&key).map(|entry| entry.value_ref()).cloned()
}
/// Reference to inner change set.
pub fn overlay(&self) -> &OverlayedMap<(Vec<u8>, Vec<u8>), OffchainOverlayedChange> {
&self.0
}
/// Mutable reference to inner change set.
pub fn overlay_mut(
&mut self,
) -> &mut OverlayedMap<(Vec<u8>, Vec<u8>), OffchainOverlayedChange> {
&mut self.0
}
}
#[cfg(test)]
mod test {
use super::*;
use pezsp_core::offchain::STORAGE_PREFIX;
#[test]
fn test_drain() {
let mut ooc = OffchainOverlayedChanges::default();
ooc.set(STORAGE_PREFIX, b"kkk", b"vvv");
let drained = ooc.drain().count();
assert_eq!(drained, 1);
let leftover = ooc.iter().count();
assert_eq!(leftover, 0);
ooc.set(STORAGE_PREFIX, b"a", b"v");
ooc.set(STORAGE_PREFIX, b"b", b"v");
ooc.set(STORAGE_PREFIX, b"c", b"v");
ooc.set(STORAGE_PREFIX, b"d", b"v");
ooc.set(STORAGE_PREFIX, b"e", b"v");
assert_eq!(ooc.iter().count(), 5);
}
#[test]
fn test_accumulated_set_remove_set() {
let mut ooc = OffchainOverlayedChanges::default();
ooc.set(STORAGE_PREFIX, b"ppp", b"qqq");
ooc.remove(STORAGE_PREFIX, b"ppp");
// keys are equiv, so it will overwrite the value and the overlay will contain
// one item
assert_eq!(ooc.iter().count(), 1);
ooc.set(STORAGE_PREFIX, b"ppp", b"rrr");
let mut iter = ooc.into_iter();
assert_eq!(
iter.next(),
Some((
(STORAGE_PREFIX.to_vec(), b"ppp".to_vec()),
OffchainOverlayedChange::SetValue(b"rrr".to_vec())
))
);
assert_eq!(iter.next(), None);
}
}
@@ -0,0 +1,245 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Read-only version of Externalities.
use crate::{Backend, StorageKey, StorageValue};
use alloc::{boxed::Box, vec::Vec};
use codec::Encode;
use core::{
any::{Any, TypeId},
marker::PhantomData,
};
use hash_db::Hasher;
use pezsp_core::{
storage::{ChildInfo, StateVersion, TrackedStorageKey},
traits::Externalities,
};
use pezsp_externalities::MultiRemovalResults;
/// Trait for inspecting state in any backend.
///
/// Implemented for any backend.
pub trait InspectState<H: Hasher, B: Backend<H>> {
/// Inspect state with a closure.
///
/// Self will be set as read-only externalities and inspection
/// closure will be run against it.
///
/// Returns the result of the closure.
fn inspect_state<F: FnOnce() -> R, R>(&self, f: F) -> R;
}
impl<H: Hasher, B: Backend<H>> InspectState<H, B> for B
where
H::Out: Encode,
{
fn inspect_state<F: FnOnce() -> R, R>(&self, f: F) -> R {
ReadOnlyExternalities::from(self).execute_with(f)
}
}
/// Simple read-only externalities for any backend.
///
/// To be used in test for state inspection. Will panic if something writes
/// to the storage.
#[derive(Debug)]
pub struct ReadOnlyExternalities<'a, H: Hasher, B: 'a + Backend<H>> {
backend: &'a B,
_phantom: PhantomData<H>,
}
impl<'a, H: Hasher, B: 'a + Backend<H>> From<&'a B> for ReadOnlyExternalities<'a, H, B> {
fn from(backend: &'a B) -> Self {
ReadOnlyExternalities { backend, _phantom: PhantomData }
}
}
impl<'a, H: Hasher, B: 'a + Backend<H>> ReadOnlyExternalities<'a, H, B>
where
H::Out: Encode,
{
/// Execute the given closure while `self` is set as externalities.
///
/// Returns the result of the given closure.
pub fn execute_with<R>(&mut self, f: impl FnOnce() -> R) -> R {
pezsp_externalities::set_and_run_with_externalities(self, f)
}
}
impl<'a, H: Hasher, B: 'a + Backend<H>> Externalities for ReadOnlyExternalities<'a, H, B>
where
H::Out: Encode,
{
fn set_offchain_storage(&mut self, _key: &[u8], _value: Option<&[u8]>) {
panic!("Should not be used in read-only externalities!")
}
fn storage(&mut self, key: &[u8]) -> Option<StorageValue> {
self.backend
.storage(key)
.expect("Backed failed for storage in ReadOnlyExternalities")
}
fn storage_hash(&mut self, key: &[u8]) -> Option<Vec<u8>> {
self.backend
.storage_hash(key)
.expect("Backed failed for storage_hash in ReadOnlyExternalities")
.map(|h| h.encode())
}
fn child_storage(&mut self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageValue> {
self.backend
.child_storage(child_info, key)
.expect("Backed failed for child_storage in ReadOnlyExternalities")
}
fn child_storage_hash(&mut self, child_info: &ChildInfo, key: &[u8]) -> Option<Vec<u8>> {
self.backend
.child_storage_hash(child_info, key)
.expect("Backed failed for child_storage_hash in ReadOnlyExternalities")
.map(|h| h.encode())
}
fn next_storage_key(&mut self, key: &[u8]) -> Option<StorageKey> {
self.backend
.next_storage_key(key)
.expect("Backed failed for next_storage_key in ReadOnlyExternalities")
}
fn next_child_storage_key(&mut self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageKey> {
self.backend
.next_child_storage_key(child_info, key)
.expect("Backed failed for next_child_storage_key in ReadOnlyExternalities")
}
fn place_storage(&mut self, _key: StorageKey, _maybe_value: Option<StorageValue>) {
unimplemented!("place_storage not supported in ReadOnlyExternalities")
}
fn place_child_storage(
&mut self,
_child_info: &ChildInfo,
_key: StorageKey,
_value: Option<StorageValue>,
) {
unimplemented!("place_child_storage not supported in ReadOnlyExternalities")
}
fn kill_child_storage(
&mut self,
_child_info: &ChildInfo,
_maybe_limit: Option<u32>,
_maybe_cursor: Option<&[u8]>,
) -> MultiRemovalResults {
unimplemented!("kill_child_storage is not supported in ReadOnlyExternalities")
}
fn clear_prefix(
&mut self,
_prefix: &[u8],
_maybe_limit: Option<u32>,
_maybe_cursor: Option<&[u8]>,
) -> MultiRemovalResults {
unimplemented!("clear_prefix is not supported in ReadOnlyExternalities")
}
fn clear_child_prefix(
&mut self,
_child_info: &ChildInfo,
_prefix: &[u8],
_maybe_limit: Option<u32>,
_maybe_cursor: Option<&[u8]>,
) -> MultiRemovalResults {
unimplemented!("clear_child_prefix is not supported in ReadOnlyExternalities")
}
fn storage_append(&mut self, _key: Vec<u8>, _value: Vec<u8>) {
unimplemented!("storage_append is not supported in ReadOnlyExternalities")
}
fn storage_root(&mut self, _state_version: StateVersion) -> Vec<u8> {
unimplemented!("storage_root is not supported in ReadOnlyExternalities")
}
fn child_storage_root(
&mut self,
_child_info: &ChildInfo,
_state_version: StateVersion,
) -> Vec<u8> {
unimplemented!("child_storage_root is not supported in ReadOnlyExternalities")
}
fn storage_start_transaction(&mut self) {
unimplemented!("Transactions are not supported by ReadOnlyExternalities");
}
fn storage_rollback_transaction(&mut self) -> Result<(), ()> {
unimplemented!("Transactions are not supported by ReadOnlyExternalities");
}
fn storage_commit_transaction(&mut self) -> Result<(), ()> {
unimplemented!("Transactions are not supported by ReadOnlyExternalities");
}
fn wipe(&mut self) {}
fn commit(&mut self) {}
fn read_write_count(&self) -> (u32, u32, u32, u32) {
unimplemented!("read_write_count is not supported in ReadOnlyExternalities")
}
fn reset_read_write_count(&mut self) {
unimplemented!("reset_read_write_count is not supported in ReadOnlyExternalities")
}
fn get_whitelist(&self) -> Vec<TrackedStorageKey> {
unimplemented!("get_whitelist is not supported in ReadOnlyExternalities")
}
fn set_whitelist(&mut self, _: Vec<TrackedStorageKey>) {
unimplemented!("set_whitelist is not supported in ReadOnlyExternalities")
}
fn get_read_and_written_keys(&self) -> Vec<(Vec<u8>, u32, u32, bool)> {
unimplemented!("get_read_and_written_keys is not supported in ReadOnlyExternalities")
}
}
impl<'a, H: Hasher, B: 'a + Backend<H>> pezsp_externalities::ExtensionStore
for ReadOnlyExternalities<'a, H, B>
{
fn extension_by_type_id(&mut self, _type_id: TypeId) -> Option<&mut dyn Any> {
unimplemented!("extension_by_type_id is not supported in ReadOnlyExternalities")
}
fn register_extension_with_type_id(
&mut self,
_type_id: TypeId,
_extension: Box<dyn pezsp_externalities::Extension>,
) -> Result<(), pezsp_externalities::Error> {
unimplemented!("register_extension_with_type_id is not supported in ReadOnlyExternalities")
}
fn deregister_extension_by_type_id(
&mut self,
_type_id: TypeId,
) -> Result<(), pezsp_externalities::Error> {
unimplemented!("deregister_extension_by_type_id is not supported in ReadOnlyExternalities")
}
}
@@ -0,0 +1,131 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Usage statistics for state db
use core::cell::RefCell;
#[cfg(feature = "std")]
use std::time::{Duration, Instant};
/// Measured count of operations and total bytes.
#[derive(Clone, Debug, Default)]
pub struct UsageUnit {
/// Number of operations.
pub ops: u64,
/// Number of bytes.
pub bytes: u64,
}
/// Usage statistics for state backend.
#[derive(Clone, Debug)]
pub struct UsageInfo {
/// Read statistics (total).
pub reads: UsageUnit,
/// Write statistics (total).
pub writes: UsageUnit,
/// Write trie nodes statistics.
pub nodes_writes: UsageUnit,
/// Write into cached state machine
/// change overlay.
pub overlay_writes: UsageUnit,
/// Removed trie nodes statistics.
pub removed_nodes: UsageUnit,
/// Cache read statistics.
pub cache_reads: UsageUnit,
/// Modified value read statistics.
pub modified_reads: UsageUnit,
/// 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,
}
/// Accumulated usage statistics specific to state machine
/// crate.
#[derive(Debug, Default, Clone)]
pub struct StateMachineStats {
/// Number of read query from runtime
/// that hit a modified value (in state
/// machine overlay).
pub reads_modified: RefCell<u64>,
/// Size in byte of read queries that
/// hit a modified value.
pub bytes_read_modified: RefCell<u64>,
/// Number of time a write operation
/// occurs into the state machine overlay.
pub writes_overlay: RefCell<u64>,
/// Size in bytes of the writes overlay
/// operation.
pub bytes_writes_overlay: RefCell<u64>,
}
impl StateMachineStats {
/// Accumulates some registered stats.
pub fn add(&self, other: &StateMachineStats) {
*self.reads_modified.borrow_mut() += *other.reads_modified.borrow();
*self.bytes_read_modified.borrow_mut() += *other.bytes_read_modified.borrow();
*self.writes_overlay.borrow_mut() += *other.writes_overlay.borrow();
*self.bytes_writes_overlay.borrow_mut() += *other.bytes_writes_overlay.borrow();
}
}
impl UsageInfo {
/// Empty statistics.
///
/// Means no data was collected.
pub fn empty() -> Self {
Self {
reads: UsageUnit::default(),
writes: UsageUnit::default(),
overlay_writes: UsageUnit::default(),
nodes_writes: UsageUnit::default(),
removed_nodes: UsageUnit::default(),
cache_reads: UsageUnit::default(),
modified_reads: UsageUnit::default(),
memory: 0,
#[cfg(feature = "std")]
started: Instant::now(),
#[cfg(feature = "std")]
span: Default::default(),
}
}
/// Add collected state machine to this state.
pub fn include_state_machine_states(&mut self, count: &StateMachineStats) {
self.modified_reads.ops += *count.reads_modified.borrow();
self.modified_reads.bytes += *count.bytes_read_modified.borrow();
self.overlay_writes.ops += *count.writes_overlay.borrow();
self.overlay_writes.bytes += *count.bytes_writes_overlay.borrow();
}
}
impl StateMachineStats {
/// Tally one read modified operation, of some length.
pub fn tally_read_modified(&self, data_bytes: u64) {
*self.reads_modified.borrow_mut() += 1;
*self.bytes_read_modified.borrow_mut() += data_bytes;
}
/// Tally one write overlay operation, of some length.
pub fn tally_write_overlay(&self, data_bytes: u64) {
*self.writes_overlay.borrow_mut() += 1;
*self.bytes_writes_overlay.borrow_mut() += data_bytes;
}
}
@@ -0,0 +1,540 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Test implementation for Externalities.
use std::{
any::{Any, TypeId},
panic::{AssertUnwindSafe, UnwindSafe},
};
use crate::{
backend::Backend, ext::Ext, InMemoryBackend, OverlayedChanges, StorageKey, StorageValue,
TrieBackendBuilder,
};
use hash_db::{HashDB, Hasher};
use pezsp_core::{
offchain::testing::TestPersistentOffchainDB,
storage::{
well_known_keys::{is_child_storage_key, CODE},
StateVersion, Storage,
},
};
use pezsp_externalities::{Extension, ExtensionStore, Extensions};
use pezsp_trie::{recorder::Recorder, PrefixedMemoryDB, StorageProof};
/// Simple HashMap-based Externalities impl.
pub struct TestExternalities<H>
where
H: Hasher + 'static,
H::Out: codec::Codec + Ord,
{
/// The overlay changed storage.
overlay: OverlayedChanges<H>,
offchain_db: TestPersistentOffchainDB,
/// Storage backend.
pub backend: InMemoryBackend<H>,
/// Extensions.
pub extensions: Extensions,
/// State version to use during tests.
pub state_version: StateVersion,
}
impl<H> TestExternalities<H>
where
H: Hasher + 'static,
H::Out: Ord + 'static + codec::Codec,
{
/// Get externalities implementation.
pub fn ext(&mut self) -> Ext<'_, H, InMemoryBackend<H>> {
Ext::new(&mut self.overlay, &self.backend, Some(&mut self.extensions))
}
/// Create a new instance of `TestExternalities` with storage.
pub fn new(storage: Storage) -> Self {
Self::new_with_code_and_state(&[], storage, Default::default())
}
/// Create a new instance of `TestExternalities` with storage for a given state version.
pub fn new_with_state_version(storage: Storage, state_version: StateVersion) -> Self {
Self::new_with_code_and_state(&[], storage, state_version)
}
/// New empty test externalities.
pub fn new_empty() -> Self {
Self::new_with_code_and_state(&[], Storage::default(), Default::default())
}
/// Create a new instance of `TestExternalities` with code and storage.
pub fn new_with_code(code: &[u8], storage: Storage) -> Self {
Self::new_with_code_and_state(code, storage, Default::default())
}
/// Create a new instance of `TestExternalities` with code and storage for a given state
/// version.
pub fn new_with_code_and_state(
code: &[u8],
mut storage: Storage,
state_version: StateVersion,
) -> Self {
assert!(storage.top.keys().all(|key| !is_child_storage_key(key)));
storage.top.insert(CODE.to_vec(), code.to_vec());
let offchain_db = TestPersistentOffchainDB::new();
let backend = (storage, state_version).into();
TestExternalities {
overlay: OverlayedChanges::default(),
offchain_db,
extensions: Default::default(),
backend,
state_version,
}
}
/// Returns the overlayed changes.
pub fn overlayed_changes(&self) -> &OverlayedChanges<H> {
&self.overlay
}
/// Move offchain changes from overlay to the persistent store.
pub fn persist_offchain_overlay(&mut self) {
self.offchain_db.apply_offchain_changes(self.overlay.offchain_drain_committed());
}
/// A shared reference type around the offchain worker storage.
pub fn offchain_db(&self) -> TestPersistentOffchainDB {
self.offchain_db.clone()
}
/// Batch insert key/values into backend
pub fn batch_insert<I>(&mut self, kvs: I)
where
I: IntoIterator<Item = (StorageKey, StorageValue)>,
{
self.backend.insert(
Some((None, kvs.into_iter().map(|(k, v)| (k, Some(v))).collect())),
self.state_version,
);
}
/// Insert key/value into backend
pub fn insert(&mut self, k: StorageKey, v: StorageValue) {
self.backend.insert(vec![(None, vec![(k, Some(v))])], self.state_version);
}
/// Insert key/value into backend.
///
/// This only supports inserting keys in child tries.
pub fn insert_child(&mut self, c: pezsp_core::storage::ChildInfo, k: StorageKey, v: StorageValue) {
self.backend.insert(vec![(Some(c), vec![(k, Some(v))])], self.state_version);
}
/// Registers the given extension for this instance.
pub fn register_extension<E: Any + Extension>(&mut self, ext: E) {
self.extensions.register(ext);
}
/// Sets raw storage key/values and a root.
///
/// This can be used as a fast way to restore the storage state from a backup because the trie
/// does not need to be computed.
pub fn from_raw_snapshot(
raw_storage: Vec<(Vec<u8>, (Vec<u8>, i32))>,
storage_root: H::Out,
state_version: StateVersion,
) -> Self {
let mut backend = PrefixedMemoryDB::default();
for (key, (v, ref_count)) in raw_storage {
let mut hash = H::Out::default();
let hash_len = hash.as_ref().len();
if key.len() < hash_len {
log::warn!("Invalid key in `from_raw_snapshot`: {key:?}");
continue;
}
hash.as_mut().copy_from_slice(&key[(key.len() - hash_len)..]);
// Each time .emplace is called the internal MemoryDb ref count increments.
// Repeatedly call emplace to initialise the ref count to the correct value.
for _ in 0..ref_count {
backend.emplace(hash, (&key[..(key.len() - hash_len)], None), v.clone());
}
}
Self {
backend: TrieBackendBuilder::new(backend, storage_root).build(),
overlay: Default::default(),
offchain_db: Default::default(),
extensions: Default::default(),
state_version,
}
}
/// Drains the underlying raw storage key/values and returns the root hash.
///
/// Useful for backing up the storage in a format that can be quickly re-loaded.
pub fn into_raw_snapshot(mut self) -> (Vec<(Vec<u8>, (Vec<u8>, i32))>, H::Out) {
let raw_key_values = self
.backend
.backend_storage_mut()
.drain()
.into_iter()
.filter(|(_, (_, r))| *r > 0)
.collect::<Vec<(Vec<u8>, (Vec<u8>, i32))>>();
(raw_key_values, *self.backend.root())
}
/// Return a new backend with all pending changes.
///
/// In contrast to [`commit_all`](Self::commit_all) this will not panic if there are open
/// transactions.
pub fn as_backend(&mut self) -> InMemoryBackend<H> {
let top: Vec<_> = self
.overlay
.changes_mut()
.map(|(k, v)| (k.clone(), v.value().cloned()))
.collect();
let mut transaction = vec![(None, top)];
for (child_changes, child_info) in self.overlay.children_mut() {
transaction.push((
Some(child_info.clone()),
child_changes.map(|(k, v)| (k.clone(), v.value().cloned())).collect(),
))
}
self.backend.update(transaction, self.state_version)
}
/// Commit all pending changes to the underlying backend.
///
/// # Panic
///
/// This will panic if there are still open transactions.
pub fn commit_all(&mut self) -> Result<(), String> {
let changes = self.overlay.drain_storage_changes(&self.backend, self.state_version)?;
self.backend
.apply_transaction(changes.transaction_storage_root, changes.transaction);
Ok(())
}
/// Execute the given closure while `self` is set as externalities.
///
/// Returns the result of the given closure.
pub fn execute_with<R>(&mut self, execute: impl FnOnce() -> R) -> R {
let mut ext = self.ext();
pezsp_externalities::set_and_run_with_externalities(&mut ext, execute)
}
/// Execute the given closure while `self`, with `proving_backend` as backend, is set as
/// externalities.
///
/// This implementation will wipe the proof recorded in between calls. Consecutive calls will
/// get their own proof from scratch.
pub fn execute_and_prove<R>(&mut self, execute: impl FnOnce() -> R) -> (R, StorageProof) {
let proving_backend = TrieBackendBuilder::wrap(&self.backend)
.with_recorder(Default::default())
.build();
let mut proving_ext =
Ext::new(&mut self.overlay, &proving_backend, Some(&mut self.extensions));
let outcome = pezsp_externalities::set_and_run_with_externalities(&mut proving_ext, execute);
let proof = proving_backend.extract_proof().expect("Failed to extract storage proof");
(outcome, proof)
}
/// Execute the given closure while `self` set as externalities and the given `proof_recorder`
/// enabled.
pub fn execute_with_recorder<R>(
&mut self,
proof_recorder: Recorder<H>,
execute: impl FnOnce() -> R,
) -> R {
let proving_backend =
TrieBackendBuilder::wrap(&self.backend).with_recorder(proof_recorder).build();
let mut proving_ext =
Ext::new(&mut self.overlay, &proving_backend, Some(&mut self.extensions));
pezsp_externalities::set_and_run_with_externalities(&mut proving_ext, execute)
}
/// Execute the given closure while `self` is set as externalities.
///
/// Returns the result of the given closure, if no panics occurred.
/// Otherwise, returns `Err`.
pub fn execute_with_safe<R>(
&mut self,
f: impl FnOnce() -> R + UnwindSafe,
) -> Result<R, String> {
let mut ext = AssertUnwindSafe(self.ext());
std::panic::catch_unwind(move || {
pezsp_externalities::set_and_run_with_externalities(&mut *ext, f)
})
.map_err(|e| format!("Closure panicked: {:?}", e))
}
/// Resets the overlay to its default state.
pub fn reset_overlay(&mut self) {
self.overlay = Default::default();
}
}
impl<H: Hasher> std::fmt::Debug for TestExternalities<H>
where
H::Out: Ord + codec::Codec,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let pairs: Vec<_> = self
.backend
.pairs(Default::default())
.expect("creating an iterator over all of the pairs doesn't fail in tests")
.collect();
write!(f, "overlay: {:?}\nbackend: {:?}", self.overlay, pairs)
}
}
impl<H> TestExternalities<H>
where
H: Hasher,
H::Out: Ord + 'static + codec::Codec,
{
/// This doesn't test if they are in the same state, only if they contains the
/// same data at this state
pub fn eq(&mut self, other: &mut TestExternalities<H>) -> bool {
self.as_backend().eq(&other.as_backend())
}
}
impl<H: Hasher> Default for TestExternalities<H>
where
H::Out: Ord + 'static + codec::Codec,
{
fn default() -> Self {
// default to default version.
Self::new_with_state_version(Storage::default(), Default::default())
}
}
impl<H: Hasher> From<Storage> for TestExternalities<H>
where
H::Out: Ord + 'static + codec::Codec,
{
fn from(storage: Storage) -> Self {
Self::new_with_state_version(storage, Default::default())
}
}
impl<H: Hasher> From<(Storage, StateVersion)> for TestExternalities<H>
where
H::Out: Ord + 'static + codec::Codec,
{
fn from((storage, state_version): (Storage, StateVersion)) -> Self {
Self::new_with_state_version(storage, state_version)
}
}
impl<H> pezsp_externalities::ExtensionStore for TestExternalities<H>
where
H: Hasher,
H::Out: Ord + codec::Codec,
{
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
self.extensions.get_mut(type_id)
}
fn register_extension_with_type_id(
&mut self,
type_id: TypeId,
extension: Box<dyn Extension>,
) -> Result<(), pezsp_externalities::Error> {
self.extensions.register_with_type_id(type_id, extension)
}
fn deregister_extension_by_type_id(
&mut self,
type_id: TypeId,
) -> Result<(), pezsp_externalities::Error> {
if self.extensions.deregister(type_id) {
Ok(())
} else {
Err(pezsp_externalities::Error::ExtensionIsNotRegistered(type_id))
}
}
}
impl<H> pezsp_externalities::ExternalitiesExt for TestExternalities<H>
where
H: Hasher,
H::Out: Ord + codec::Codec,
{
fn extension<T: Any + Extension>(&mut self) -> Option<&mut T> {
self.extension_by_type_id(TypeId::of::<T>()).and_then(<dyn Any>::downcast_mut)
}
fn register_extension<T: Extension>(&mut self, ext: T) -> Result<(), pezsp_externalities::Error> {
self.register_extension_with_type_id(TypeId::of::<T>(), Box::new(ext))
}
fn deregister_extension<T: Extension>(&mut self) -> Result<(), pezsp_externalities::Error> {
self.deregister_extension_by_type_id(TypeId::of::<T>())
}
}
#[cfg(test)]
mod tests {
use super::*;
use pezsp_core::{storage::ChildInfo, traits::Externalities, H256};
use pezsp_runtime::traits::BlakeTwo256;
#[test]
fn commit_should_work() {
let storage = Storage::default(); // avoid adding the trie threshold.
let mut ext = TestExternalities::<BlakeTwo256>::from((storage, Default::default()));
let mut ext = ext.ext();
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
ext.set_storage(b"dog".to_vec(), b"puppy".to_vec());
ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec());
let root = array_bytes::hex_n_into_unchecked::<_, H256, 32>(
"ed4d8c799d996add422395a6abd7545491d40bd838d738afafa1b8a4de625489",
);
assert_eq!(H256::from_slice(ext.storage_root(Default::default()).as_slice()), root);
}
#[test]
fn raw_storage_drain_and_restore() {
// Create a TestExternalities with some data in it.
let mut original_ext =
TestExternalities::<BlakeTwo256>::from((Default::default(), Default::default()));
original_ext.insert(b"doe".to_vec(), b"reindeer".to_vec());
original_ext.insert(b"dog".to_vec(), b"puppy".to_vec());
original_ext.insert(b"dogglesworth".to_vec(), b"cat".to_vec());
let child_info = ChildInfo::new_default(&b"test_child"[..]);
original_ext.insert_child(child_info.clone(), b"cattytown".to_vec(), b"is_dark".to_vec());
original_ext.insert_child(child_info.clone(), b"doggytown".to_vec(), b"is_sunny".to_vec());
// Apply the backend to itself again to increase the ref count of all nodes.
original_ext.backend.apply_transaction(
*original_ext.backend.root(),
original_ext.backend.clone().into_storage(),
);
// Ensure all have the correct ref count
assert!(original_ext.backend.backend_storage().keys().values().all(|r| *r == 2));
// Drain the raw storage and root.
let root = *original_ext.backend.root();
let (raw_storage, storage_root) = original_ext.into_raw_snapshot();
// Load the raw storage and root into a new TestExternalities.
let recovered_ext = TestExternalities::<BlakeTwo256>::from_raw_snapshot(
raw_storage,
storage_root,
Default::default(),
);
// Check the storage root is the same as the original
assert_eq!(root, *recovered_ext.backend.root());
// Check the original storage key/values were recovered correctly
assert_eq!(recovered_ext.backend.storage(b"doe").unwrap(), Some(b"reindeer".to_vec()));
assert_eq!(recovered_ext.backend.storage(b"dog").unwrap(), Some(b"puppy".to_vec()));
assert_eq!(recovered_ext.backend.storage(b"dogglesworth").unwrap(), Some(b"cat".to_vec()));
// Check the original child storage key/values were recovered correctly
assert_eq!(
recovered_ext.backend.child_storage(&child_info, b"cattytown").unwrap(),
Some(b"is_dark".to_vec())
);
assert_eq!(
recovered_ext.backend.child_storage(&child_info, b"doggytown").unwrap(),
Some(b"is_sunny".to_vec())
);
// Ensure all have the correct ref count after importing
assert!(recovered_ext.backend.backend_storage().keys().values().all(|r| *r == 2));
}
#[test]
fn set_and_retrieve_code() {
let mut ext = TestExternalities::<BlakeTwo256>::default();
let mut ext = ext.ext();
let code = vec![1, 2, 3];
ext.set_storage(CODE.to_vec(), code.clone());
assert_eq!(&ext.storage(CODE).unwrap(), &code);
}
#[test]
fn check_send() {
fn assert_send<T: Send>() {}
assert_send::<TestExternalities<BlakeTwo256>>();
}
#[test]
fn commit_all_and_kill_child_storage() {
let mut ext = TestExternalities::<BlakeTwo256>::default();
let child_info = ChildInfo::new_default(&b"test_child"[..]);
{
let mut ext = ext.ext();
ext.place_child_storage(&child_info, b"doe".to_vec(), Some(b"reindeer".to_vec()));
ext.place_child_storage(&child_info, b"dog".to_vec(), Some(b"puppy".to_vec()));
ext.place_child_storage(&child_info, b"dog2".to_vec(), Some(b"puppy2".to_vec()));
}
ext.commit_all().unwrap();
{
let mut ext = ext.ext();
assert!(
ext.kill_child_storage(&child_info, Some(2), None).maybe_cursor.is_some(),
"Should not delete all keys"
);
assert!(ext.child_storage(&child_info, &b"doe"[..]).is_none());
assert!(ext.child_storage(&child_info, &b"dog"[..]).is_none());
assert!(ext.child_storage(&child_info, &b"dog2"[..]).is_some());
}
}
#[test]
fn as_backend_generates_same_backend_as_commit_all() {
let mut ext = TestExternalities::<BlakeTwo256>::default();
{
let mut ext = ext.ext();
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
ext.set_storage(b"dog".to_vec(), b"puppy".to_vec());
ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec());
}
let backend = ext.as_backend();
ext.commit_all().unwrap();
assert!(ext.backend.eq(&backend), "Both backend should be equal.");
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,942 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Trie-based state machine backend essence used to read values
//! from storage.
use crate::{
backend::{IterArgs, StorageIterator},
trie_backend::TrieCacheProvider,
warn, StorageKey, StorageValue,
};
#[cfg(feature = "std")]
use alloc::sync::Arc;
use alloc::{boxed::Box, vec::Vec};
use codec::Codec;
use core::marker::PhantomData;
use hash_db::{self, AsHashDB, HashDB, HashDBRef, Hasher, Prefix};
#[cfg(feature = "std")]
use parking_lot::RwLock;
use pezsp_core::storage::{ChildInfo, ChildType, StateVersion};
use pezsp_trie::{
child_delta_trie_root, delta_trie_root, empty_child_trie_root,
read_child_trie_first_descendant_value, read_child_trie_hash, read_child_trie_value,
read_trie_first_descendant_value, read_trie_value,
trie_types::{TrieDBBuilder, TrieError},
DBValue, KeySpacedDB, MerkleValue, NodeCodec, PrefixedMemoryDB, RandomState, Trie, TrieCache,
TrieDBRawIterator, TrieRecorder, TrieRecorderProvider,
};
#[cfg(feature = "std")]
use std::collections::HashMap;
// In this module, we only use layout for read operation and empty root,
// where V1 and V0 are equivalent.
use pezsp_trie::LayoutV1 as Layout;
#[cfg(not(feature = "std"))]
macro_rules! format {
( $message:expr, $( $arg:expr )* ) => {
{
$( let _ = &$arg; )*
crate::DefaultError
}
};
}
type Result<V> = core::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>>;
}
/// Local cache for child root.
#[cfg(feature = "std")]
pub(crate) struct Cache<H> {
pub child_root: HashMap<Vec<u8>, Option<H>>,
}
#[cfg(feature = "std")]
impl<H> Cache<H> {
fn new() -> Self {
Cache { child_root: HashMap::new() }
}
}
enum IterState {
Pending,
FinishedComplete,
FinishedIncomplete,
}
/// A raw iterator over the storage.
pub struct RawIter<S, H, C, R>
where
H: Hasher,
{
stop_on_incomplete_database: bool,
skip_if_first: Option<StorageKey>,
root: H::Out,
child_info: Option<ChildInfo>,
trie_iter: TrieDBRawIterator<Layout<H>>,
state: IterState,
_phantom: PhantomData<(S, C, R)>,
}
impl<S, H, C, R> RawIter<S, H, C, R>
where
H: Hasher,
S: TrieBackendStorage<H>,
H::Out: Codec + Ord,
C: TrieCacheProvider<H> + Send + Sync,
R: TrieRecorderProvider<H> + Send + Sync,
{
#[inline]
fn prepare<RE>(
&mut self,
backend: &TrieBackendEssence<S, H, C, R>,
callback: impl FnOnce(
&pezsp_trie::TrieDB<Layout<H>>,
&mut TrieDBRawIterator<Layout<H>>,
) -> Option<core::result::Result<RE, Box<TrieError<<H as Hasher>::Out>>>>,
) -> Option<Result<RE>> {
if !matches!(self.state, IterState::Pending) {
return None;
}
let result = backend.with_trie_db(self.root, self.child_info.as_ref(), |db| {
callback(&db, &mut self.trie_iter)
});
match result {
Some(Ok(key_value)) => Some(Ok(key_value)),
None => {
self.state = IterState::FinishedComplete;
None
},
Some(Err(error)) => {
if matches!(*error, TrieError::IncompleteDatabase(_)) &&
self.stop_on_incomplete_database
{
self.state = IterState::FinishedIncomplete;
None
} else {
Some(Err(format!("TrieDB iteration error: {}", error)))
}
},
}
}
}
impl<S, H, C, R> Default for RawIter<S, H, C, R>
where
H: Hasher,
{
fn default() -> Self {
Self {
stop_on_incomplete_database: false,
skip_if_first: None,
child_info: None,
root: Default::default(),
trie_iter: TrieDBRawIterator::empty(),
state: IterState::FinishedComplete,
_phantom: Default::default(),
}
}
}
impl<S, H, C, R> StorageIterator<H> for RawIter<S, H, C, R>
where
H: Hasher,
S: TrieBackendStorage<H>,
H::Out: Codec + Ord,
C: TrieCacheProvider<H> + Send + Sync,
R: TrieRecorderProvider<H> + Send + Sync,
{
type Backend = crate::TrieBackend<S, H, C, R>;
type Error = crate::DefaultError;
#[inline]
fn next_key(&mut self, backend: &Self::Backend) -> Option<Result<StorageKey>> {
let skip_if_first = self.skip_if_first.take();
self.prepare(&backend.essence, |trie, trie_iter| {
let mut result = trie_iter.next_key(&trie);
if let Some(skipped_key) = skip_if_first {
if let Some(Ok(ref key)) = result {
if *key == skipped_key {
result = trie_iter.next_key(&trie);
}
}
}
result
})
}
#[inline]
fn next_pair(&mut self, backend: &Self::Backend) -> Option<Result<(StorageKey, StorageValue)>> {
let skip_if_first = self.skip_if_first.take();
self.prepare(&backend.essence, |trie, trie_iter| {
let mut result = trie_iter.next_item(&trie);
if let Some(skipped_key) = skip_if_first {
if let Some(Ok((ref key, _))) = result {
if *key == skipped_key {
result = trie_iter.next_item(&trie);
}
}
}
result
})
}
fn was_complete(&self) -> bool {
matches!(self.state, IterState::FinishedComplete)
}
}
/// Patricia trie-based pairs storage essence.
pub struct TrieBackendEssence<S: TrieBackendStorage<H>, H: Hasher, C, R> {
storage: S,
root: H::Out,
empty: H::Out,
#[cfg(feature = "std")]
pub(crate) cache: Arc<RwLock<Cache<H::Out>>>,
pub(crate) trie_node_cache: Option<C>,
pub(crate) recorder: Option<R>,
}
impl<S: TrieBackendStorage<H>, H: Hasher, C, R> TrieBackendEssence<S, H, C, R> {
/// Create new trie-based backend.
pub fn new(storage: S, root: H::Out) -> Self {
Self::new_with_cache(storage, root, None)
}
/// Create new trie-based backend.
pub fn new_with_cache(storage: S, root: H::Out, cache: Option<C>) -> Self {
TrieBackendEssence {
storage,
root,
empty: H::hash(&[0u8]),
#[cfg(feature = "std")]
cache: Arc::new(RwLock::new(Cache::new())),
trie_node_cache: cache,
recorder: None,
}
}
/// Create new trie-based backend.
pub fn new_with_cache_and_recorder(
storage: S,
root: H::Out,
cache: Option<C>,
recorder: Option<R>,
) -> Self {
TrieBackendEssence {
storage,
root,
empty: H::hash(&[0u8]),
#[cfg(feature = "std")]
cache: Arc::new(RwLock::new(Cache::new())),
trie_node_cache: cache,
recorder,
}
}
/// Get backend storage reference.
pub fn backend_storage(&self) -> &S {
&self.storage
}
/// Get backend storage mutable reference.
pub fn backend_storage_mut(&mut self) -> &mut S {
&mut self.storage
}
/// Get trie root.
pub fn root(&self) -> &H::Out {
&self.root
}
/// Set trie root. This is useful for testing.
pub fn set_root(&mut self, root: H::Out) {
// If root did change so can have cached content.
self.reset_cache();
self.root = root;
}
#[cfg(feature = "std")]
fn reset_cache(&mut self) {
self.cache = Arc::new(RwLock::new(Cache::new()));
}
#[cfg(not(feature = "std"))]
fn reset_cache(&mut self) {}
/// Consumes self and returns underlying storage.
pub fn into_storage(self) -> S {
self.storage
}
}
impl<S: TrieBackendStorage<H>, H: Hasher, C: TrieCacheProvider<H>, R: TrieRecorderProvider<H>>
TrieBackendEssence<S, H, C, R>
{
/// Call the given closure passing it the recorder and the cache.
///
/// If the given `storage_root` is `None`, `self.root` will be used.
#[inline]
fn with_recorder_and_cache<RE>(
&self,
storage_root: Option<H::Out>,
callback: impl FnOnce(
Option<&mut dyn TrieRecorder<H::Out>>,
Option<&mut dyn TrieCache<NodeCodec<H>>>,
) -> RE,
) -> RE {
let storage_root = storage_root.unwrap_or_else(|| self.root);
let mut cache = self.trie_node_cache.as_ref().map(|c| c.as_trie_db_cache(storage_root));
let cache = cache.as_mut().map(|c| c as _);
let mut recorder = self.recorder.as_ref().map(|r| r.as_trie_recorder(storage_root));
let recorder = match recorder.as_mut() {
Some(recorder) => Some(recorder as &mut dyn TrieRecorder<H::Out>),
None => None,
};
callback(recorder, cache)
}
/// Call the given closure passing it the recorder and the cache.
///
/// This function must only be used when the operation in `callback` is
/// calculating a `storage_root`. It is expected that `callback` returns
/// the new storage root. This is required to register the changes in the cache
/// for the correct storage root. The given `storage_root` corresponds to the root of the "old"
/// trie. If the value is not given, `self.root` is used.
fn with_recorder_and_cache_for_storage_root<RE>(
&self,
storage_root: Option<H::Out>,
callback: impl FnOnce(
Option<&mut dyn TrieRecorder<H::Out>>,
Option<&mut dyn TrieCache<NodeCodec<H>>>,
) -> (Option<H::Out>, RE),
) -> RE {
let storage_root = storage_root.unwrap_or_else(|| self.root);
let mut recorder = self.recorder.as_ref().map(|r| r.as_trie_recorder(storage_root));
let recorder = match recorder.as_mut() {
Some(recorder) => Some(recorder as &mut dyn TrieRecorder<H::Out>),
None => None,
};
let result = if let Some(local_cache) = self.trie_node_cache.as_ref() {
let mut cache = local_cache.as_trie_db_mut_cache();
let (new_root, r) = callback(recorder, Some(&mut cache));
if let Some(new_root) = new_root {
local_cache.merge(cache, new_root);
}
r
} else {
callback(recorder, None).1
};
result
}
}
impl<
S: TrieBackendStorage<H>,
H: Hasher,
C: TrieCacheProvider<H> + Send + Sync,
R: TrieRecorderProvider<H> + Send + Sync,
> TrieBackendEssence<S, H, C, R>
where
H::Out: Codec + Ord,
{
/// Calls the given closure with a [`TrieDb`] constructed for the given
/// storage root and (optionally) child trie.
#[inline]
fn with_trie_db<RE>(
&self,
root: H::Out,
child_info: Option<&ChildInfo>,
callback: impl FnOnce(&pezsp_trie::TrieDB<Layout<H>>) -> RE,
) -> RE {
let backend = self as &dyn HashDBRef<H, Vec<u8>>;
let db = child_info
.as_ref()
.map(|child_info| KeySpacedDB::new(backend, child_info.keyspace()));
let db = db.as_ref().map(|db| db as &dyn HashDBRef<H, Vec<u8>>).unwrap_or(backend);
self.with_recorder_and_cache(Some(root), |recorder, cache| {
let trie = TrieDBBuilder::<H>::new(db, &root)
.with_optional_recorder(recorder)
.with_optional_cache(cache)
.build();
callback(&trie)
})
}
/// Returns the next key in the trie i.e. the minimum key that is strictly superior to `key` in
/// lexicographic order.
///
/// Will always traverse the trie from scratch in search of the key, which is slow.
/// Used only when debug assertions are enabled to crosscheck the results of finding
/// the next key through an iterator.
#[cfg(debug_assertions)]
pub fn next_storage_key_slow(&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<H::Out>> {
#[cfg(feature = "std")]
{
if let Some(result) = self.cache.read().child_root.get(child_info.storage_key()) {
return Ok(*result);
}
}
let result = self.storage(child_info.prefixed_storage_key().as_slice())?.map(|r| {
let mut hash = H::Out::default();
// root is fetched from DB, not writable by runtime, so it's always valid.
hash.as_mut().copy_from_slice(&r[..]);
hash
});
#[cfg(feature = "std")]
{
self.cache.write().child_root.insert(child_info.storage_key().to_vec(), result);
}
Ok(result)
}
/// Return the next key in the child trie i.e. the minimum key that is strictly superior to
/// `key` in lexicographic order.
pub fn next_child_storage_key(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<StorageKey>> {
let child_root = match self.child_root(child_info)? {
Some(child_root) => child_root,
None => return Ok(None),
};
self.next_storage_key_from_root(&child_root, Some(child_info), key)
}
/// Return next key from main trie or child trie by providing corresponding root.
fn next_storage_key_from_root(
&self,
root: &H::Out,
child_info: Option<&ChildInfo>,
key: &[u8],
) -> Result<Option<StorageKey>> {
self.with_trie_db(*root, child_info, |trie| {
let mut iter = trie.key_iter().map_err(|e| format!("TrieDB iteration error: {}", e))?;
// The key just after the one given in input, basically `key++0`.
// Note: We are sure this is the next key if:
// * size of key has no limit (i.e. we can always add 0 to the path),
// * and no keys can be inserted between `key` and `key++0` (this is ensured by sp-io).
let mut potential_next_key = Vec::with_capacity(key.len() + 1);
potential_next_key.extend_from_slice(key);
potential_next_key.push(0);
iter.seek(&potential_next_key)
.map_err(|e| format!("TrieDB iterator seek error: {}", e))?;
let next_element = iter.next();
let next_key = if let Some(next_element) = next_element {
let next_key =
next_element.map_err(|e| format!("TrieDB iterator next error: {}", e))?;
Some(next_key)
} else {
None
};
Ok(next_key)
})
}
/// Returns the hash value
pub fn storage_hash(&self, key: &[u8]) -> Result<Option<H::Out>> {
let map_e = |e| format!("Trie lookup error: {}", e);
self.with_recorder_and_cache(None, |recorder, cache| {
TrieDBBuilder::new(self, &self.root)
.with_optional_cache(cache)
.with_optional_recorder(recorder)
.build()
.get_hash(key)
.map_err(map_e)
})
}
/// Get the value of storage at given key.
pub fn storage(&self, key: &[u8]) -> Result<Option<StorageValue>> {
let map_e = |e| format!("Trie lookup error: {}", e);
self.with_recorder_and_cache(None, |recorder, cache| {
read_trie_value::<Layout<H>, _>(self, &self.root, key, recorder, cache).map_err(map_e)
})
}
/// Returns the hash value
pub fn child_storage_hash(&self, child_info: &ChildInfo, key: &[u8]) -> Result<Option<H::Out>> {
let child_root = match self.child_root(child_info)? {
Some(root) => root,
None => return Ok(None),
};
let map_e = |e| format!("Trie lookup error: {}", e);
self.with_recorder_and_cache(Some(child_root), |recorder, cache| {
read_child_trie_hash::<Layout<H>, _>(
child_info.keyspace(),
self,
&child_root,
key,
recorder,
cache,
)
.map_err(map_e)
})
}
/// Get the value of child storage at given key.
pub fn child_storage(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<StorageValue>> {
let child_root = match self.child_root(child_info)? {
Some(root) => root,
None => return Ok(None),
};
let map_e = |e| format!("Trie lookup error: {}", e);
self.with_recorder_and_cache(Some(child_root), |recorder, cache| {
read_child_trie_value::<Layout<H>, _>(
child_info.keyspace(),
self,
&child_root,
key,
recorder,
cache,
)
.map_err(map_e)
})
}
/// Get the closest merkle value at given key.
pub fn closest_merkle_value(&self, key: &[u8]) -> Result<Option<MerkleValue<H::Out>>> {
let map_e = |e| format!("Trie lookup error: {}", e);
self.with_recorder_and_cache(None, |recorder, cache| {
read_trie_first_descendant_value::<Layout<H>, _>(self, &self.root, key, recorder, cache)
.map_err(map_e)
})
}
/// Get the child closest merkle value at given key.
pub fn child_closest_merkle_value(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<MerkleValue<H::Out>>> {
let Some(child_root) = self.child_root(child_info)? else { return Ok(None) };
let map_e = |e| format!("Trie lookup error: {}", e);
self.with_recorder_and_cache(Some(child_root), |recorder, cache| {
read_child_trie_first_descendant_value::<Layout<H>, _>(
child_info.keyspace(),
self,
&child_root,
key,
recorder,
cache,
)
.map_err(map_e)
})
}
/// Create a raw iterator over the storage.
pub fn raw_iter(&self, args: IterArgs) -> Result<RawIter<S, H, C, R>> {
let root = if let Some(child_info) = args.child_info.as_ref() {
let root = match self.child_root(&child_info)? {
Some(root) => root,
None => return Ok(Default::default()),
};
root
} else {
self.root
};
if self.root == Default::default() {
// A special-case for an empty storage root.
return Ok(Default::default());
}
let trie_iter = self
.with_trie_db(root, args.child_info.as_ref(), |db| {
let prefix = args.prefix.as_deref().unwrap_or(&[]);
if let Some(start_at) = args.start_at {
TrieDBRawIterator::new_prefixed_then_seek(db, prefix, &start_at)
} else {
TrieDBRawIterator::new_prefixed(db, prefix)
}
})
.map_err(|e| format!("TrieDB iteration error: {}", e))?;
Ok(RawIter {
stop_on_incomplete_database: args.stop_on_incomplete_database,
skip_if_first: if args.start_at_exclusive {
args.start_at.map(|key| key.to_vec())
} else {
None
},
child_info: args.child_info,
root,
trie_iter,
state: IterState::Pending,
_phantom: Default::default(),
})
}
/// Return the storage root after applying the given `delta`.
pub fn storage_root<'a>(
&self,
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
state_version: StateVersion,
) -> (H::Out, PrefixedMemoryDB<H>) {
let mut write_overlay = PrefixedMemoryDB::with_hasher(RandomState::default());
let root = self.with_recorder_and_cache_for_storage_root(None, |recorder, cache| {
let mut eph = Ephemeral::new(self.backend_storage(), &mut write_overlay);
let res = match state_version {
StateVersion::V0 => delta_trie_root::<pezsp_trie::LayoutV0<H>, _, _, _, _, _>(
&mut eph, self.root, delta, recorder, cache,
),
StateVersion::V1 => delta_trie_root::<pezsp_trie::LayoutV1<H>, _, _, _, _, _>(
&mut eph, self.root, delta, recorder, cache,
),
};
match res {
Ok(ret) => (Some(ret), ret),
Err(e) => {
warn!(target: "trie", "Failed to write to trie: {}", e);
(None, self.root)
},
}
});
(root, write_overlay)
}
/// Returns the child storage root for the child trie `child_info` after applying the given
/// `delta`.
pub fn child_storage_root<'a>(
&self,
child_info: &ChildInfo,
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
state_version: StateVersion,
) -> (H::Out, bool, PrefixedMemoryDB<H>) {
let default_root = match child_info.child_type() {
ChildType::ParentKeyId => empty_child_trie_root::<pezsp_trie::LayoutV1<H>>(),
};
let mut write_overlay = PrefixedMemoryDB::with_hasher(RandomState::default());
let child_root = match self.child_root(child_info) {
Ok(Some(hash)) => hash,
Ok(None) => default_root,
Err(e) => {
warn!(target: "trie", "Failed to read child storage root: {}", e);
default_root
},
};
let new_child_root =
self.with_recorder_and_cache_for_storage_root(Some(child_root), |recorder, cache| {
let mut eph = Ephemeral::new(self.backend_storage(), &mut write_overlay);
match match state_version {
StateVersion::V0 =>
child_delta_trie_root::<pezsp_trie::LayoutV0<H>, _, _, _, _, _, _>(
child_info.keyspace(),
&mut eph,
child_root,
delta,
recorder,
cache,
),
StateVersion::V1 =>
child_delta_trie_root::<pezsp_trie::LayoutV1<H>, _, _, _, _, _, _>(
child_info.keyspace(),
&mut eph,
child_root,
delta,
recorder,
cache,
),
} {
Ok(ret) => (Some(ret), ret),
Err(e) => {
warn!(target: "trie", "Failed to write to trie: {}", e);
(None, child_root)
},
}
});
let is_default = new_child_root == default_root;
(new_child_root, is_default, write_overlay)
}
}
pub(crate) struct Ephemeral<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> {
storage: &'a S,
overlay: &'a mut PrefixedMemoryDB<H>,
}
impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> AsHashDB<H, DBValue>
for Ephemeral<'a, S, H>
{
fn as_hash_db<'b>(&'b self) -> &'b (dyn HashDB<H, DBValue> + 'b) {
self
}
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn HashDB<H, DBValue> + 'b) {
self
}
}
impl<'a, S: TrieBackendStorage<H>, H: Hasher> Ephemeral<'a, S, H> {
pub fn new(storage: &'a S, overlay: &'a mut PrefixedMemoryDB<H>) -> Self {
Ephemeral { storage, overlay }
}
}
impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue>
for Ephemeral<'a, S, H>
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
HashDB::get(self.overlay, key, prefix).or_else(|| {
self.storage.get(key, prefix).unwrap_or_else(|e| {
warn!(target: "trie", "Failed to read from DB: {}", e);
None
})
})
}
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
HashDB::get(self, key, prefix).is_some()
}
fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out {
HashDB::insert(self.overlay, prefix, value)
}
fn emplace(&mut self, key: H::Out, prefix: Prefix, value: DBValue) {
HashDB::emplace(self.overlay, key, prefix, value)
}
fn remove(&mut self, key: &H::Out, prefix: Prefix) {
HashDB::remove(self.overlay, key, prefix)
}
}
impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> HashDBRef<H, DBValue> for Ephemeral<'a, S, H> {
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
HashDB::get(self, key, prefix)
}
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
HashDB::contains(self, key, prefix)
}
}
/// Key-value pairs storage that is used by trie backend essence.
pub trait TrieBackendStorage<H: Hasher>: Send + Sync {
/// Get the value stored at key.
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>>;
}
impl<T: TrieBackendStorage<H>, H: Hasher> TrieBackendStorage<H> for &T {
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
(*self).get(key, prefix)
}
}
// This implementation is used by normal storage trie clients.
#[cfg(feature = "std")]
impl<H: Hasher> TrieBackendStorage<H> for Arc<dyn Storage<H>> {
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
Storage::<H>::get(std::ops::Deref::deref(self), key, prefix)
}
}
impl<H, KF> TrieBackendStorage<H> for pezsp_trie::GenericMemoryDB<H, KF>
where
H: Hasher,
KF: pezsp_trie::KeyFunction<H> + Send + Sync,
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>> {
Ok(hash_db::HashDB::get(self, key, prefix))
}
}
impl<
S: TrieBackendStorage<H>,
H: Hasher,
C: TrieCacheProvider<H> + Send + Sync,
R: TrieRecorderProvider<H> + Send + Sync,
> AsHashDB<H, DBValue> for TrieBackendEssence<S, H, C, R>
{
fn as_hash_db<'b>(&'b self) -> &'b (dyn HashDB<H, DBValue> + 'b) {
self
}
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn HashDB<H, DBValue> + 'b) {
self
}
}
impl<
S: TrieBackendStorage<H>,
H: Hasher,
C: TrieCacheProvider<H> + Send + Sync,
R: TrieRecorderProvider<H> + Send + Sync,
> HashDB<H, DBValue> for TrieBackendEssence<S, H, C, R>
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
if *key == self.empty {
return Some([0u8].to_vec());
}
match self.storage.get(key, prefix) {
Ok(x) => x,
Err(e) => {
warn!(target: "trie", "Failed to read from DB: {}", e);
None
},
}
}
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
HashDB::get(self, key, prefix).is_some()
}
fn insert(&mut self, _prefix: Prefix, _value: &[u8]) -> H::Out {
unimplemented!();
}
fn emplace(&mut self, _key: H::Out, _prefix: Prefix, _value: DBValue) {
unimplemented!();
}
fn remove(&mut self, _key: &H::Out, _prefix: Prefix) {
unimplemented!();
}
}
impl<
S: TrieBackendStorage<H>,
H: Hasher,
C: TrieCacheProvider<H> + Send + Sync,
R: TrieRecorderProvider<H> + Send + Sync,
> HashDBRef<H, DBValue> for TrieBackendEssence<S, H, C, R>
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
HashDB::get(self, key, prefix)
}
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
HashDB::contains(self, key, prefix)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{Backend, TrieBackend};
use pezsp_core::{Blake2Hasher, H256};
use pezsp_trie::{
cache::LocalTrieCache, trie_types::TrieDBMutBuilderV1 as TrieDBMutBuilder, KeySpacedDBMut,
PrefixedMemoryDB, TrieMut,
};
#[test]
fn next_storage_key_and_next_child_storage_key_work() {
let child_info = ChildInfo::new_default(b"MyChild");
let child_info = &child_info;
// Contains values
let mut root_1 = H256::default();
// Contains child trie
let mut root_2 = H256::default();
let mut mdb = PrefixedMemoryDB::<Blake2Hasher>::default();
{
let mut trie = TrieDBMutBuilder::new(&mut mdb, &mut root_1).build();
trie.insert(b"3", &[1]).expect("insert failed");
trie.insert(b"4", &[1]).expect("insert failed");
trie.insert(b"6", &[1]).expect("insert failed");
}
{
let mut mdb = KeySpacedDBMut::new(&mut mdb, child_info.keyspace());
// reuse of root_1 implicitly assert child trie root is same
// as top trie (contents must remain the same).
let mut trie = TrieDBMutBuilder::new(&mut mdb, &mut root_1).build();
trie.insert(b"3", &[1]).expect("insert failed");
trie.insert(b"4", &[1]).expect("insert failed");
trie.insert(b"6", &[1]).expect("insert failed");
}
{
let mut trie = TrieDBMutBuilder::new(&mut mdb, &mut root_2).build();
trie.insert(child_info.prefixed_storage_key().as_slice(), root_1.as_ref())
.expect("insert failed");
};
let essence_1 =
TrieBackendEssence::<_, _, LocalTrieCache<_>, pezsp_trie::recorder::Recorder<_>>::new(
mdb, root_1,
);
let mdb = essence_1.backend_storage().clone();
let essence_1 = TrieBackend::from_essence(essence_1);
assert_eq!(essence_1.next_storage_key(b"2"), Ok(Some(b"3".to_vec())));
assert_eq!(essence_1.next_storage_key(b"3"), Ok(Some(b"4".to_vec())));
assert_eq!(essence_1.next_storage_key(b"4"), Ok(Some(b"6".to_vec())));
assert_eq!(essence_1.next_storage_key(b"5"), Ok(Some(b"6".to_vec())));
assert_eq!(essence_1.next_storage_key(b"6"), Ok(None));
let essence_2 =
TrieBackendEssence::<_, _, LocalTrieCache<_>, pezsp_trie::recorder::Recorder<_>>::new(
mdb, root_2,
);
assert_eq!(essence_2.next_child_storage_key(child_info, b"2"), Ok(Some(b"3".to_vec())));
assert_eq!(essence_2.next_child_storage_key(child_info, b"3"), Ok(Some(b"4".to_vec())));
assert_eq!(essence_2.next_child_storage_key(child_info, b"4"), Ok(Some(b"6".to_vec())));
assert_eq!(essence_2.next_child_storage_key(child_info, b"5"), Ok(Some(b"6".to_vec())));
assert_eq!(essence_2.next_child_storage_key(child_info, b"6"), Ok(None));
}
}