Move Externalities into its own crate (#3775)

* Move `Externalities` into `substrate-externalities`

- `Externalities` now support generic extensions
- Split of `primtives-storage` for storage primitive types

* Move the externalities scoping into `substrate-externalities`

* Fix compilation

* Review feedback

* Adds macro for declaring extensions

* Fix benchmarks

* Introduce `ExtensionStore` trait

* Last review comments

* Implement it for `ExtensionStore`
This commit is contained in:
Bastian Köcher
2019-10-09 15:50:30 +02:00
committed by GitHub
parent 984c6ac839
commit 8a39be474e
95 changed files with 1600 additions and 1420 deletions
+34 -27
View File
@@ -16,15 +16,14 @@
//! Basic implementation for Externalities.
use std::collections::HashMap;
use std::iter::FromIterator;
use std::{collections::HashMap, any::{TypeId, Any}, iter::FromIterator};
use crate::backend::{Backend, InMemory};
use hash_db::Hasher;
use trie::{TrieConfiguration, default_child_trie_root};
use trie::trie_types::Layout;
use primitives::{
storage::well_known_keys::is_child_storage_key, child_storage_key::ChildStorageKey, offchain,
traits::Externalities,
storage::{well_known_keys::is_child_storage_key, ChildStorageKey},
traits::Externalities, Blake2Hasher, hash::H256,
};
use log::warn;
@@ -88,21 +87,37 @@ impl From<HashMap<Vec<u8>, Vec<u8>>> for BasicExternalities {
}
}
impl<H: Hasher> Externalities<H> for BasicExternalities where H::Out: Ord {
impl Externalities for BasicExternalities {
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
self.top.get(key).cloned()
}
fn storage_hash(&self, key: &[u8]) -> Option<H256> {
self.storage(key).map(|v| Blake2Hasher::hash(&v))
}
fn original_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
Externalities::<H>::storage(self, key)
self.storage(key)
}
fn original_storage_hash(&self, key: &[u8]) -> Option<H256> {
self.storage_hash(key)
}
fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
self.children.get(storage_key.as_ref()).and_then(|child| child.get(key)).cloned()
}
fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<H256> {
self.child_storage(storage_key, key).map(|v| Blake2Hasher::hash(&v))
}
fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<H256> {
self.child_storage_hash(storage_key, key)
}
fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
Externalities::<H>::child_storage(self, storage_key, key)
Externalities::child_storage(self, storage_key, key)
}
fn place_storage(&mut self, key: Vec<u8>, maybe_value: Option<Vec<u8>>) {
@@ -155,16 +170,15 @@ impl<H: Hasher> Externalities<H> for BasicExternalities where H::Out: Ord {
fn chain_id(&self) -> u64 { 42 }
fn storage_root(&mut self) -> H::Out {
fn storage_root(&mut self) -> H256 {
let mut top = self.top.clone();
let keys: Vec<_> = self.children.keys().map(|k| k.to_vec()).collect();
// 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 = default_child_trie_root::<Layout<H>>(&[]);
let empty_hash = default_child_trie_root::<Layout<Blake2Hasher>>(&[]);
for storage_key in keys {
let child_root = Externalities::<H>::child_storage_root(
self,
let child_root = self.child_storage_root(
ChildStorageKey::from_slice(storage_key.as_slice())
.expect("Map only feed by valid keys; qed"),
);
@@ -175,30 +189,27 @@ impl<H: Hasher> Externalities<H> for BasicExternalities where H::Out: Ord {
}
}
Layout::<H>::trie_root(self.top.clone())
Layout::<Blake2Hasher>::trie_root(self.top.clone())
}
fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec<u8> {
if let Some(child) = self.children.get(storage_key.as_ref()) {
let delta = child.clone().into_iter().map(|(k, v)| (k, Some(v)));
InMemory::<H>::default().child_storage_root(storage_key.as_ref(), delta).0
InMemory::<Blake2Hasher>::default().child_storage_root(storage_key.as_ref(), delta).0
} else {
default_child_trie_root::<Layout<H>>(storage_key.as_ref())
default_child_trie_root::<Layout<Blake2Hasher>>(storage_key.as_ref())
}
}
fn storage_changes_root(&mut self, _parent: H::Out) -> Result<Option<H::Out>, ()> {
fn storage_changes_root(&mut self, _parent: H256) -> Result<Option<H256>, ()> {
Ok(None)
}
}
fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> {
warn!("Call to non-existent offchain externalities set.");
None
}
fn keystore(&self) -> Option<primitives::traits::BareCryptoStorePtr> {
warn!("Call to non-existent keystore.");
impl externalities::ExtensionStore for BasicExternalities {
fn extension_by_type_id(&mut self, _: TypeId) -> Option<&mut dyn Any> {
warn!("Extensions are not supported by `BasicExternalities`.");
None
}
}
@@ -206,14 +217,13 @@ impl<H: Hasher> Externalities<H> for BasicExternalities where H::Out: Ord {
#[cfg(test)]
mod tests {
use super::*;
use primitives::{Blake2Hasher, H256, map};
use primitives::{H256, map};
use primitives::storage::well_known_keys::CODE;
use hex_literal::hex;
#[test]
fn commit_should_work() {
let mut ext = BasicExternalities::default();
let ext = &mut ext as &mut dyn Externalities<Blake2Hasher>;
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());
@@ -225,7 +235,6 @@ mod tests {
#[test]
fn set_and_retrieve_code() {
let mut ext = BasicExternalities::default();
let ext = &mut ext as &mut dyn Externalities<Blake2Hasher>;
let code = vec![1, 2, 3];
ext.set_storage(CODE.to_vec(), code.clone());
@@ -246,8 +255,6 @@ mod tests {
]
);
let ext = &mut ext as &mut dyn Externalities<Blake2Hasher>;
let child = || ChildStorageKey::from_vec(child_storage.clone()).unwrap();
assert_eq!(ext.child_storage(child(), b"doe"), Some(b"reindeer".to_vec()));
+98 -71
View File
@@ -16,22 +16,24 @@
//! Concrete externalities implementation.
use std::{error, fmt, cmp::Ord};
use log::{warn, trace};
use crate::{
backend::Backend, OverlayedChanges,
changes_trie::{
Storage as ChangesTrieStorage, CacheAction as ChangesTrieCacheAction, build_changes_trie,
},
};
use hash_db::Hasher;
use primitives::{
offchain, storage::well_known_keys::is_child_storage_key,
traits::{BareCryptoStorePtr, Externalities}, child_storage_key::ChildStorageKey,
hexdisplay::HexDisplay,
storage::{ChildStorageKey, well_known_keys::is_child_storage_key},
traits::Externalities, hexdisplay::HexDisplay, hash::H256,
};
use trie::{MemoryDB, default_child_trie_root};
use trie::trie_types::Layout;
use trie::{trie_types::Layout, MemoryDB, default_child_trie_root};
use externalities::Extensions;
use std::{error, fmt, any::{Any, TypeId}};
use log::{warn, trace};
const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime";
@@ -65,11 +67,7 @@ impl<B: error::Error, E: error::Error> error::Error for Error<B, E> {
}
/// Wraps a read-only backend, call executor, and current overlayed changes.
pub struct Ext<'a, H, N, B, T, O>
where
H: Hasher,
B: 'a + Backend<H>,
{
pub struct Ext<'a, H, N, B, T> where H: Hasher<Out=H256>, B: 'a + Backend<H> {
/// The overlayed changes to write to.
overlay: &'a mut OverlayedChanges,
/// The storage backend to read from.
@@ -86,25 +84,19 @@ where
/// `storage_changes_root` is called matters + we need to remember additional
/// data at this moment (block number).
changes_trie_transaction: Option<(MemoryDB<H>, H::Out, ChangesTrieCacheAction<H::Out, N>)>,
/// Additional externalities for offchain workers.
///
/// If None, some methods from the trait might not be supported.
offchain_externalities: Option<&'a mut O>,
/// The keystore that manages the keys of the node.
keystore: Option<BareCryptoStorePtr>,
/// Pseudo-unique id used for tracing.
pub id: u16,
/// Dummy usage of N arg.
_phantom: ::std::marker::PhantomData<N>,
_phantom: std::marker::PhantomData<N>,
/// Extensions registered with this instance.
extensions: Option<&'a mut Extensions>,
}
impl<'a, H, N, B, T, O> Ext<'a, H, N, B, T, O>
impl<'a, H, N, B, T> Ext<'a, H, N, B, T>
where
H: Hasher,
H: Hasher<Out=H256>,
B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H, N>,
O: 'a + offchain::Externalities,
H::Out: Ord + 'static,
N: crate::changes_trie::BlockNumber,
{
/// Create a new `Ext` from overlayed changes and read-only backend
@@ -112,8 +104,7 @@ where
overlay: &'a mut OverlayedChanges,
backend: &'a B,
changes_trie_storage: Option<&'a T>,
offchain_externalities: Option<&'a mut O>,
keystore: Option<BareCryptoStorePtr>,
extensions: Option<&'a mut Extensions>,
) -> Self {
Ext {
overlay,
@@ -121,21 +112,25 @@ where
storage_transaction: None,
changes_trie_storage,
changes_trie_transaction: None,
offchain_externalities,
keystore,
id: rand::random(),
_phantom: Default::default(),
extensions,
}
}
/// Get the transaction necessary to update the backend.
pub fn transaction(mut self) -> ((B::Transaction, H::Out), Option<crate::ChangesTrieTransaction<H, N>>) {
pub fn transaction(&mut self) -> (
(B::Transaction, H256),
Option<crate::ChangesTrieTransaction<H, N>>,
) {
let _ = self.storage_root();
let (storage_transaction, changes_trie_transaction) = (
self.storage_transaction
.take()
.expect("storage_transaction always set after calling storage root; qed"),
self.changes_trie_transaction
.take()
.map(|(tx, _, cache)| (tx, cache)),
);
@@ -151,16 +146,14 @@ where
fn mark_dirty(&mut self) {
self.storage_transaction = None;
}
}
#[cfg(test)]
impl<'a, H, N, B, T, O> Ext<'a, H, N, B, T, O>
impl<'a, H, N, B, T> Ext<'a, H, N, B, T>
where
H: Hasher,
H: Hasher<Out=H256>,
B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H, N>,
O: 'a + offchain::Externalities,
N: crate::changes_trie::BlockNumber,
{
pub fn storage_pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
@@ -177,13 +170,12 @@ where
}
}
impl<'a, B, T, H, N, O> Externalities<H> for Ext<'a, H, N, B, T, O>
where H: Hasher,
impl<'a, H, B, T, N> Externalities for Ext<'a, H, N, B, T>
where
H: Hasher<Out=H256>,
B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H, N>,
H::Out: Ord + 'static,
N: crate::changes_trie::BlockNumber,
O: 'a + offchain::Externalities,
{
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::force_abort();
@@ -197,10 +189,14 @@ where H: Hasher,
result
}
fn storage_hash(&self, key: &[u8]) -> Option<H::Out> {
fn storage_hash(&self, key: &[u8]) -> Option<H256> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.overlay.storage(key).map(|x| x.map(|x| H::hash(x))).unwrap_or_else(||
self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL));
let result = self.overlay
.storage(key)
.map(|x| x.map(|x| H::hash(x)))
.unwrap_or_else(||
self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL)
);
trace!(target: "state-trace", "{:04x}: Hash {}={:?}",
self.id,
HexDisplay::from(&key),
@@ -220,7 +216,7 @@ where H: Hasher,
result
}
fn original_storage_hash(&self, key: &[u8]) -> Option<H::Out> {
fn original_storage_hash(&self, key: &[u8]) -> Option<H256> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL);
trace!(target: "state-trace", "{:04x}: GetOriginalHash {}={:?}",
@@ -233,33 +229,48 @@ where H: Hasher,
fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.overlay.child_storage(storage_key.as_ref(), key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL));
let result = self.overlay
.child_storage(storage_key.as_ref(), key)
.map(|x| x.map(|x| x.to_vec()))
.unwrap_or_else(||
self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL)
);
trace!(target: "state-trace", "{:04x}: GetChild({}) {}={:?}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
HexDisplay::from(&key),
result.as_ref().map(HexDisplay::from)
);
result
}
fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<H::Out> {
fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<H256> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.overlay.child_storage(storage_key.as_ref(), key).map(|x| x.map(|x| H::hash(x))).unwrap_or_else(||
self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL));
let result = self.overlay
.child_storage(storage_key.as_ref(), key)
.map(|x| x.map(|x| H::hash(x)))
.unwrap_or_else(||
self.backend.storage_hash(key).expect(EXT_NOT_ALLOWED_TO_FAIL)
);
trace!(target: "state-trace", "{:04x}: ChildHash({}) {}={:?}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
HexDisplay::from(&key),
result,
);
result
}
fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.backend.child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL);
let result = self.backend
.child_storage(storage_key.as_ref(), key)
.expect(EXT_NOT_ALLOWED_TO_FAIL);
trace!(target: "state-trace", "{:04x}: ChildOriginal({}) {}={:?}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
@@ -269,9 +280,12 @@ where H: Hasher,
result
}
fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<H::Out> {
fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<H256> {
let _guard = panic_handler::AbortGuard::force_abort();
let result = self.backend.child_storage_hash(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL);
let result = self.backend
.child_storage_hash(storage_key.as_ref(), key)
.expect(EXT_NOT_ALLOWED_TO_FAIL);
trace!(target: "state-trace", "{}: ChildHashOriginal({}) {}={:?}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
@@ -287,6 +301,7 @@ where H: Hasher,
Some(x) => x.is_some(),
_ => self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL),
};
trace!(target: "state-trace", "{:04x}: Exists {}={:?}",
self.id,
HexDisplay::from(&key),
@@ -301,8 +316,11 @@ where H: Hasher,
let result = match self.overlay.child_storage(storage_key.as_ref(), key) {
Some(x) => x.is_some(),
_ => self.backend.exists_child_storage(storage_key.as_ref(), key).expect(EXT_NOT_ALLOWED_TO_FAIL),
_ => self.backend
.exists_child_storage(storage_key.as_ref(), key)
.expect(EXT_NOT_ALLOWED_TO_FAIL),
};
trace!(target: "state-trace", "{:04x}: ChildExists({}) {}={:?}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
@@ -328,7 +346,12 @@ where H: Hasher,
self.overlay.set_storage(key, value);
}
fn place_child_storage(&mut self, storage_key: ChildStorageKey, key: Vec<u8>, value: Option<Vec<u8>>) {
fn place_child_storage(
&mut self,
storage_key: ChildStorageKey,
key: Vec<u8>,
value: Option<Vec<u8>>,
) {
trace!(target: "state-trace", "{:04x}: PutChild({}) {}={:?}",
self.id,
HexDisplay::from(&storage_key.as_ref()),
@@ -392,7 +415,7 @@ where H: Hasher,
42
}
fn storage_root(&mut self) -> H::Out {
fn storage_root(&mut self) -> H256 {
let _guard = panic_handler::AbortGuard::force_abort();
if let Some((_, ref root)) = self.storage_transaction {
trace!(target: "state-trace", "{:04x}: Root (cached) {}",
@@ -465,7 +488,7 @@ where H: Hasher,
}
}
fn storage_changes_root(&mut self, parent_hash: H::Out) -> Result<Option<H::Out>, ()> {
fn storage_changes_root(&mut self, parent_hash: H256) -> Result<Option<H256>, ()> {
let _guard = panic_handler::AbortGuard::force_abort();
self.changes_trie_transaction = build_changes_trie::<_, T, H, N>(
self.backend,
@@ -481,32 +504,36 @@ where H: Hasher,
);
result
}
}
fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> {
self.offchain_externalities.as_mut().map(|x| &mut **x as _)
impl<'a, H, B, T, N> externalities::ExtensionStore for Ext<'a, H, N, B, T>
where
H: Hasher<Out=H256>,
B: 'a + Backend<H>,
T: 'a + ChangesTrieStorage<H, N>,
N: crate::changes_trie::BlockNumber,
{
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
self.extensions.as_mut().and_then(|exts| exts.get_mut(type_id))
}
fn keystore(&self) -> Option<BareCryptoStorePtr> {
self.keystore.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
use codec::Encode;
use primitives::{Blake2Hasher};
use primitives::storage::well_known_keys::EXTRINSIC_INDEX;
use crate::backend::InMemory;
use crate::changes_trie::{Configuration as ChangesTrieConfiguration,
InMemoryStorage as InMemoryChangesTrieStorage};
use crate::overlayed_changes::OverlayedValue;
use super::*;
use primitives::{Blake2Hasher, storage::well_known_keys::EXTRINSIC_INDEX};
use crate::{
changes_trie::{
Configuration as ChangesTrieConfiguration,
InMemoryStorage as InMemoryChangesTrieStorage,
}, backend::InMemory, overlayed_changes::OverlayedValue,
};
type TestBackend = InMemory<Blake2Hasher>;
type TestChangesTrieStorage = InMemoryChangesTrieStorage<Blake2Hasher, u64>;
type TestExt<'a> = Ext<'a, Blake2Hasher, u64, TestBackend, TestChangesTrieStorage, crate::NeverOffchainExt>;
type TestExt<'a> = Ext<'a, Blake2Hasher, u64, TestBackend, TestChangesTrieStorage>;
fn prepare_overlay_with_changes() -> OverlayedChanges {
OverlayedChanges {
@@ -532,7 +559,7 @@ mod tests {
fn storage_changes_root_is_none_when_storage_is_not_provided() {
let mut overlay = prepare_overlay_with_changes();
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, None, None, None);
let mut ext = TestExt::new(&mut overlay, &backend, None, None);
assert_eq!(ext.storage_changes_root(Default::default()).unwrap(), None);
}
@@ -542,7 +569,7 @@ mod tests {
overlay.changes_trie_config = None;
let storage = TestChangesTrieStorage::with_blocks(vec![(100, Default::default())]);
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None);
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
assert_eq!(ext.storage_changes_root(Default::default()).unwrap(), None);
}
@@ -551,7 +578,7 @@ mod tests {
let mut overlay = prepare_overlay_with_changes();
let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]);
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None);
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
assert_eq!(
ext.storage_changes_root(Default::default()).unwrap(),
Some(hex!("bb0c2ef6e1d36d5490f9766cfcc7dfe2a6ca804504c3bb206053890d6dd02376").into()),
@@ -564,7 +591,7 @@ mod tests {
overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None;
let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]);
let backend = TestBackend::default();
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None);
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
assert_eq!(
ext.storage_changes_root(Default::default()).unwrap(),
Some(hex!("96f5aae4690e7302737b6f9b7f8567d5bbb9eac1c315f80101235a92d9ec27f4").into()),
+100 -84
View File
@@ -18,18 +18,16 @@
#![warn(missing_docs)]
use std::{
fmt, result, collections::HashMap,
marker::PhantomData, panic::UnwindSafe,
};
use std::{fmt, result, collections::HashMap, panic::UnwindSafe, marker::PhantomData};
use log::{warn, trace};
use hash_db::Hasher;
use codec::{Decode, Encode};
use primitives::{
storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain::{self, NeverOffchainExt},
traits::{BareCryptoStorePtr, CodeExecutor},
hexdisplay::HexDisplay,
storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain::OffchainExt,
traits::{KeystoreExt, CodeExecutor}, hexdisplay::HexDisplay, hash::H256,
};
use overlayed_changes::OverlayedChangeSet;
use externalities::Extensions;
pub mod backend;
mod changes_trie;
@@ -42,9 +40,7 @@ mod proving_backend;
mod trie_backend;
mod trie_backend_essence;
use overlayed_changes::OverlayedChangeSet;
pub use trie::{TrieMut, DBValue, MemoryDB};
pub use trie::trie_types::{Layout, TrieDBMut};
pub use trie::{trie_types::{Layout, TrieDBMut}, TrieMut, DBValue, MemoryDB};
pub use testing::TestExternalities;
pub use basic::BasicExternalities;
pub use ext::Ext;
@@ -167,48 +163,54 @@ fn always_untrusted_wasm<E, R: Decode>() -> ExecutionManager<DefaultHandler<R, E
}
/// The substrate state machine.
pub struct StateMachine<'a, B, H, N, T, O, Exec> {
backend: B,
changes_trie_storage: Option<&'a T>,
offchain_ext: Option<&'a mut O>,
overlay: &'a mut OverlayedChanges,
pub struct StateMachine<'a, B, H, N, T, Exec> where H: Hasher<Out=H256>, B: Backend<H> {
backend: &'a B,
exec: &'a Exec,
method: &'a str,
call_data: &'a [u8],
keystore: Option<BareCryptoStorePtr>,
_hasher: PhantomData<(H, N)>,
overlay: &'a mut OverlayedChanges,
extensions: Extensions,
changes_trie_storage: Option<&'a T>,
_marker: PhantomData<(H, N)>,
}
impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where
H: Hasher,
Exec: CodeExecutor<H>,
impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
H: Hasher<Out=H256>,
Exec: CodeExecutor,
B: Backend<H>,
T: ChangesTrieStorage<H, N>,
O: offchain::Externalities,
H::Out: Ord + 'static,
N: crate::changes_trie::BlockNumber,
{
/// Creates new substrate state machine.
pub fn new(
backend: B,
backend: &'a B,
changes_trie_storage: Option<&'a T>,
offchain_ext: Option<&'a mut O>,
offchain_ext: Option<OffchainExt>,
overlay: &'a mut OverlayedChanges,
exec: &'a Exec,
method: &'a str,
call_data: &'a [u8],
keystore: Option<BareCryptoStorePtr>,
keystore: Option<KeystoreExt>,
) -> Self {
let mut extensions = Extensions::new();
if let Some(keystore) = keystore {
extensions.register(keystore);
}
if let Some(offchain) = offchain_ext {
extensions.register(offchain);
}
Self {
backend,
changes_trie_storage,
offchain_ext,
overlay,
exec,
method,
call_data,
keystore,
_hasher: PhantomData,
extensions,
overlay,
changes_trie_storage,
_marker: PhantomData,
}
}
@@ -220,10 +222,10 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where
///
/// Note: changes to code will be in place if this call is made again. For running partial
/// blocks (e.g. a transaction at a time), ensure a different method is used.
pub fn execute(
&mut self,
strategy: ExecutionStrategy,
) -> Result<(Vec<u8>, (B::Transaction, H::Out), Option<ChangesTrieTransaction<H, N>>), Box<dyn Error>> {
pub fn execute(&mut self, strategy: ExecutionStrategy) -> Result<
(Vec<u8>, (B::Transaction, H::Out), Option<ChangesTrieTransaction<H, N>>),
Box<dyn Error>,
> {
// We are not giving a native call and thus we are sure that the result can never be a native
// value.
self.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
@@ -252,38 +254,44 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result<R, String> + UnwindSafe,
{
let mut externalities = ext::Ext::new(
let mut ext = Ext::new(
self.overlay,
&self.backend,
self.changes_trie_storage,
self.offchain_ext.as_mut().map(|x| &mut **x),
self.keystore.clone(),
self.backend,
self.changes_trie_storage.clone(),
Some(&mut self.extensions),
);
let id = externalities.id;
trace!(target: "state-trace", "{:04x}: Call {} at {:?}. Input={:?}",
let id = ext.id;
trace!(
target: "state-trace", "{:04x}: Call {} at {:?}. Input={:?}",
id,
self.method,
self.backend,
HexDisplay::from(&self.call_data),
);
let (result, was_native) = self.exec.call(
&mut externalities,
&mut ext,
self.method,
self.call_data,
use_native,
native_call,
);
let (storage_delta, changes_delta) = if compute_tx {
let (storage_delta, changes_delta) = externalities.transaction();
let (storage_delta, changes_delta) = ext.transaction();
(Some(storage_delta), changes_delta)
} else {
(None, None)
};
trace!(target: "state-trace", "{:04x}: Return. Native={:?}, Result={:?}",
trace!(
target: "state-trace", "{:04x}: Return. Native={:?}, Result={:?}",
id,
was_native,
result,
);
(result, was_native, storage_delta, changes_delta)
}
@@ -293,12 +301,16 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where
mut native_call: Option<NC>,
orig_prospective: OverlayedChangeSet,
on_consensus_failure: Handler,
) -> (CallResult<R, Exec::Error>, Option<(B::Transaction, H::Out)>, Option<ChangesTrieTransaction<H, N>>) where
) -> (
CallResult<R, Exec::Error>,
Option<(B::Transaction, H::Out)>,
Option<ChangesTrieTransaction<H, N>>,
) where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result<R, String> + UnwindSafe,
Handler: FnOnce(
CallResult<R, Exec::Error>,
CallResult<R, Exec::Error>
CallResult<R, Exec::Error>,
) -> CallResult<R, Exec::Error>
{
let (result, was_native, storage_delta, changes_delta) = self.execute_aux(
@@ -317,7 +329,8 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where
if (result.is_ok() && wasm_result.is_ok()
&& result.as_ref().ok() == wasm_result.as_ref().ok())
|| result.is_err() && wasm_result.is_err() {
|| result.is_err() && wasm_result.is_err()
{
(result, storage_delta, changes_delta)
} else {
(on_consensus_failure(wasm_result, result), wasm_storage_delta, wasm_changes_delta)
@@ -332,7 +345,11 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where
compute_tx: bool,
mut native_call: Option<NC>,
orig_prospective: OverlayedChangeSet,
) -> (CallResult<R, Exec::Error>, Option<(B::Transaction, H::Out)>, Option<ChangesTrieTransaction<H, N>>) where
) -> (
CallResult<R, Exec::Error>,
Option<(B::Transaction, H::Out)>,
Option<ChangesTrieTransaction<H, N>>,
) where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result<R, String> + UnwindSafe,
{
@@ -431,7 +448,7 @@ impl<'a, B, H, N, T, O, Exec> StateMachine<'a, B, H, N, T, O, Exec> where
};
if result.is_ok() {
init_overlay(self.overlay, true, &self.backend)?;
init_overlay(self.overlay, true, self.backend)?;
}
result.map_err(|e| Box::new(e) as _)
@@ -445,13 +462,12 @@ pub fn prove_execution<B, H, Exec>(
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option<BareCryptoStorePtr>,
keystore: Option<KeystoreExt>,
) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn Error>>
where
B: Backend<H>,
H: Hasher,
Exec: CodeExecutor<H>,
H::Out: Ord + 'static,
H: Hasher<Out=H256>,
Exec: CodeExecutor,
{
let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?;
@@ -473,17 +489,16 @@ pub fn prove_execution_on_trie_backend<S, H, Exec>(
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option<BareCryptoStorePtr>,
keystore: Option<KeystoreExt>,
) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn Error>>
where
S: trie_backend_essence::TrieBackendStorage<H>,
H: Hasher,
Exec: CodeExecutor<H>,
H::Out: Ord + 'static,
H: Hasher<Out=H256>,
Exec: CodeExecutor,
{
let proving_backend = proving_backend::ProvingBackend::new(trie_backend);
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage<H, u64>, _, Exec>::new(
proving_backend, None, NeverOffchainExt::new(), overlay, exec, method, call_data, keystore,
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage<H, u64>, Exec>::new(
&proving_backend, None, None, overlay, exec, method, call_data, keystore,
);
let (result, _, _) = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
@@ -503,11 +518,11 @@ pub fn execution_proof_check<H, Exec>(
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option<BareCryptoStorePtr>,
keystore: Option<KeystoreExt>,
) -> Result<Vec<u8>, Box<dyn Error>>
where
H: Hasher,
Exec: CodeExecutor<H>,
H: Hasher<Out=H256>,
Exec: CodeExecutor,
H::Out: Ord + 'static,
{
let trie_backend = create_proof_check_backend::<H>(root.into(), proof)?;
@@ -521,15 +536,14 @@ pub fn execution_proof_check_on_trie_backend<H, Exec>(
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option<BareCryptoStorePtr>,
keystore: Option<KeystoreExt>,
) -> Result<Vec<u8>, Box<dyn Error>>
where
H: Hasher,
Exec: CodeExecutor<H>,
H::Out: Ord + 'static,
H: Hasher<Out=H256>,
Exec: CodeExecutor,
{
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage<H, u64>, _, Exec>::new(
trie_backend, None, NeverOffchainExt::new(), overlay, exec, method, call_data, keystore,
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage<H, u64>, Exec>::new(
trie_backend, None, None, overlay, exec, method, call_data, keystore,
);
sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
@@ -546,7 +560,7 @@ pub fn prove_read<B, H, I>(
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
where
B: Backend<H>,
H: Hasher,
H: Hasher<Out=H256>,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
@@ -741,7 +755,7 @@ mod tests {
InMemoryStorage as InMemoryChangesTrieStorage,
Configuration as ChangesTrieConfig,
};
use primitives::{Blake2Hasher, map, traits::Externalities, child_storage_key::ChildStorageKey};
use primitives::{Blake2Hasher, map, traits::Externalities, storage::ChildStorageKey};
struct DummyCodeExecutor {
change_changes_trie_config: bool,
@@ -750,10 +764,14 @@ mod tests {
fallback_succeeds: bool,
}
impl<H: Hasher> CodeExecutor<H> for DummyCodeExecutor {
impl CodeExecutor for DummyCodeExecutor {
type Error = u8;
fn call<E: Externalities<H>, R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result<R, String>>(
fn call<
E: Externalities,
R: Encode + Decode + PartialEq,
NC: FnOnce() -> result::Result<R, String>,
>(
&self,
ext: &mut E,
_method: &str,
@@ -800,9 +818,9 @@ mod tests {
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
let mut state_machine = StateMachine::new(
backend,
&backend,
Some(&changes_trie_storage),
NeverOffchainExt::new(),
None,
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: false,
@@ -829,9 +847,9 @@ mod tests {
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
let mut state_machine = StateMachine::new(
backend,
&backend,
Some(&changes_trie_storage),
NeverOffchainExt::new(),
None,
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: false,
@@ -855,9 +873,9 @@ mod tests {
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
let mut state_machine = StateMachine::new(
backend,
&backend,
Some(&changes_trie_storage),
NeverOffchainExt::new(),
None,
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: false,
@@ -948,7 +966,6 @@ mod tests {
&mut overlay,
backend,
Some(&changes_trie_storage),
NeverOffchainExt::new(),
None,
);
ext.clear_prefix(b"ab");
@@ -979,7 +996,6 @@ mod tests {
&mut overlay,
backend,
Some(&changes_trie_storage),
NeverOffchainExt::new(),
None,
);
@@ -1067,9 +1083,9 @@ mod tests {
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
let mut state_machine = StateMachine::new(
backend,
&backend,
Some(&changes_trie_storage),
NeverOffchainExt::new(),
None,
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: true,
@@ -1092,9 +1108,9 @@ mod tests {
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
let mut state_machine = StateMachine::new(
backend,
&backend,
Some(&changes_trie_storage),
NeverOffchainExt::new(),
None,
&mut overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: true,
@@ -420,7 +420,6 @@ mod tests {
&mut overlay,
&backend,
Some(&changes_trie_storage),
crate::NeverOffchainExt::new(),
None,
);
const ROOT: [u8; 32] = hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
@@ -432,9 +431,12 @@ mod tests {
fn changes_trie_configuration_is_saved() {
let mut overlay = OverlayedChanges::default();
assert!(overlay.changes_trie_config.is_none());
assert_eq!(overlay.set_changes_trie_config(ChangesTrieConfig {
digest_interval: 4, digest_levels: 1,
}), true);
assert_eq!(
overlay.set_changes_trie_config(
ChangesTrieConfig { digest_interval: 4, digest_levels: 1, },
),
true,
);
assert!(overlay.changes_trie_config.is_some());
}
@@ -250,7 +250,7 @@ mod tests {
use crate::backend::{InMemory};
use crate::trie_backend::tests::test_trie;
use super::*;
use primitives::{Blake2Hasher, child_storage_key::ChildStorageKey};
use primitives::{Blake2Hasher, storage::ChildStorageKey};
fn test_proving<'a>(
trie_backend: &'a TrieBackend<PrefixedMemoryDB<Blake2Hasher>,Blake2Hasher>,
+46 -38
View File
@@ -16,7 +16,7 @@
//! Test implementation for Externalities.
use std::collections::{HashMap};
use std::{collections::HashMap, any::{Any, TypeId}};
use hash_db::Hasher;
use crate::{
backend::{InMemory, Backend}, OverlayedChanges,
@@ -26,25 +26,28 @@ use crate::{
},
};
use primitives::{
storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key},
traits::{BareCryptoStorePtr, Externalities}, offchain, child_storage_key::ChildStorageKey,
storage::{
ChildStorageKey,
well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key}
},
traits::Externalities, hash::H256, Blake2Hasher,
};
use codec::Encode;
use externalities::{Extensions, Extension};
const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime";
type StorageTuple = (HashMap<Vec<u8>, Vec<u8>>, HashMap<Vec<u8>, HashMap<Vec<u8>, Vec<u8>>>);
/// Simple HashMap-based Externalities impl.
pub struct TestExternalities<H: Hasher, N: ChangesTrieBlockNumber> {
pub struct TestExternalities<H: Hasher<Out=H256>=Blake2Hasher, N: ChangesTrieBlockNumber=u64> {
overlay: OverlayedChanges,
backend: InMemory<H>,
changes_trie_storage: ChangesTrieInMemoryStorage<H, N>,
offchain: Option<Box<dyn offchain::Externalities>>,
keystore: Option<BareCryptoStorePtr>,
extensions: Extensions,
}
impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
/// Create a new instance of `TestExternalities` with storage.
pub fn new(storage: StorageTuple) -> Self {
Self::new_with_code(&[], storage)
@@ -75,8 +78,7 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
overlay,
changes_trie_storage: ChangesTrieInMemoryStorage::new(),
backend: backend.into(),
offchain: None,
keystore: None,
extensions: Default::default(),
}
}
@@ -85,14 +87,9 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
self.backend = self.backend.update(vec![(None, k, Some(v))]);
}
/// Set offchain externaltiies.
pub fn set_offchain_externalities(&mut self, offchain: impl offchain::Externalities + 'static) {
self.offchain = Some(Box::new(offchain));
}
/// Set keystore.
pub fn set_keystore(&mut self, keystore: BareCryptoStorePtr) {
self.keystore = Some(keystore);
/// Registers the given extension for this instance.
pub fn register_extension<E: Any + Extension>(&mut self, ext: E) {
self.extensions.register(ext);
}
/// Get mutable reference to changes trie storage.
@@ -118,13 +115,13 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
}
}
impl<H: Hasher, N: ChangesTrieBlockNumber> std::fmt::Debug for TestExternalities<H, N> {
impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> std::fmt::Debug for TestExternalities<H, N> {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "overlay: {:?}\nbackend: {:?}", self.overlay, self.backend.pairs())
}
}
impl<H: Hasher, N: ChangesTrieBlockNumber> PartialEq for TestExternalities<H, N> {
impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> PartialEq for TestExternalities<H, N> {
/// This doesn't test if they are in the same state, only if they contains the
/// same data at this state
fn eq(&self, other: &TestExternalities<H, N>) -> bool {
@@ -132,30 +129,37 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> PartialEq for TestExternalities<H, N>
}
}
impl<H: Hasher, N: ChangesTrieBlockNumber> Default for TestExternalities<H, N> {
impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> Default for TestExternalities<H, N> {
fn default() -> Self { Self::new(Default::default()) }
}
impl<H: Hasher, N: ChangesTrieBlockNumber> From<StorageTuple> for TestExternalities<H, N> {
impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> From<StorageTuple> for TestExternalities<H, N> {
fn from(storage: StorageTuple) -> Self {
Self::new(storage)
}
}
impl<H, N> Externalities<H> for TestExternalities<H, N> where
H: Hasher,
impl<H, N> Externalities for TestExternalities<H, N> where
H: Hasher<Out=H256>,
N: ChangesTrieBlockNumber,
H::Out: Ord + 'static,
{
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL))
}
fn storage_hash(&self, key: &[u8]) -> Option<H256> {
self.storage(key).map(|v| H::hash(&v))
}
fn original_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL)
}
fn original_storage_hash(&self, key: &[u8]) -> Option<H256> {
self.storage_hash(key)
}
fn child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
self.overlay
.child_storage(storage_key.as_ref(), key)
@@ -166,6 +170,10 @@ impl<H, N> Externalities<H> for TestExternalities<H, N> where
)
}
fn child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<H256> {
self.child_storage(storage_key, key).map(|v| H::hash(&v))
}
fn original_child_storage(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<Vec<u8>> {
self.backend
.child_storage(storage_key.as_ref(), key)
@@ -173,6 +181,10 @@ impl<H, N> Externalities<H> for TestExternalities<H, N> where
.expect(EXT_NOT_ALLOWED_TO_FAIL)
}
fn original_child_storage_hash(&self, storage_key: ChildStorageKey, key: &[u8]) -> Option<H256> {
self.child_storage_hash(storage_key, key)
}
fn place_storage(&mut self, key: Vec<u8>, maybe_value: Option<Vec<u8>>) {
if is_child_storage_key(&key) {
panic!("Refuse to directly set child storage key");
@@ -185,7 +197,7 @@ impl<H, N> Externalities<H> for TestExternalities<H, N> where
&mut self,
storage_key: ChildStorageKey,
key: Vec<u8>,
value: Option<Vec<u8>>
value: Option<Vec<u8>>,
) {
self.overlay.set_child_storage(storage_key.into_owned(), key, value);
}
@@ -215,7 +227,6 @@ impl<H, N> Externalities<H> for TestExternalities<H, N> where
}
fn clear_child_prefix(&mut self, storage_key: ChildStorageKey, prefix: &[u8]) {
self.overlay.clear_child_prefix(storage_key.as_ref(), prefix);
let backend = &self.backend;
@@ -227,8 +238,7 @@ impl<H, N> Externalities<H> for TestExternalities<H, N> where
fn chain_id(&self) -> u64 { 42 }
fn storage_root(&mut self) -> H::Out {
fn storage_root(&mut self) -> H256 {
let child_storage_keys =
self.overlay.prospective.children.keys()
.chain(self.overlay.committed.children.keys());
@@ -246,7 +256,6 @@ impl<H, N> Externalities<H> for TestExternalities<H, N> where
let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))
.chain(self.overlay.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone())));
self.backend.full_storage_root(delta, child_delta_iter).0
}
fn child_storage_root(&mut self, storage_key: ChildStorageKey) -> Vec<u8> {
@@ -270,7 +279,7 @@ impl<H, N> Externalities<H> for TestExternalities<H, N> where
root
}
fn storage_changes_root(&mut self, parent: H::Out) -> Result<Option<H::Out>, ()> {
fn storage_changes_root(&mut self, parent: H256) -> Result<Option<H256>, ()> {
Ok(build_changes_trie::<_, _, H, N>(
&self.backend,
Some(&self.changes_trie_storage),
@@ -278,15 +287,14 @@ impl<H, N> Externalities<H> for TestExternalities<H, N> where
parent,
)?.map(|(_, root, _)| root))
}
}
fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> {
self.offchain
.as_mut()
.map(|x| &mut **x as _)
}
fn keystore(&self) -> Option<BareCryptoStorePtr> {
self.keystore.clone()
impl<H, N> externalities::ExtensionStore for TestExternalities<H, N> where
H: Hasher<Out=H256>,
N: ChangesTrieBlockNumber,
{
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
self.extensions.get_mut(type_id)
}
}