Remove runtime registered extensions after execution (#7236)

* Remove runtime registered extensions after execution

This prevents a bug when an extension was registered in native, but the
native execution aborted without removing the extension again. Now, when
executing the wasm code the extension is still registered and it fails
of being registered. So, the wasm execution fails as well. This can
happen for example for the `VerificationExt`.

* Make it better
This commit is contained in:
Bastian Köcher
2020-10-01 11:12:05 +02:00
committed by GitHub
parent 8656a34289
commit dbf4558633
7 changed files with 180 additions and 39 deletions
@@ -168,16 +168,20 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
if capabilities.has(offchain::Capability::TransactionPool) {
if let Some(pool) = self.transaction_pool.read().as_ref().and_then(|x| x.upgrade()) {
extensions.register(TransactionPoolExt(Box::new(TransactionPoolAdapter {
at: *at,
pool,
}) as _));
extensions.register(
TransactionPoolExt(
Box::new(TransactionPoolAdapter {
at: *at,
pool,
}) as _
),
);
}
}
if let ExecutionContext::OffchainCall(Some(ext)) = context {
extensions.register(
OffchainExt::new(offchain::LimitedExternalities::new(capabilities, ext.0))
OffchainExt::new(offchain::LimitedExternalities::new(capabilities, ext.0)),
);
}
@@ -123,14 +123,25 @@ impl Extensions {
}
/// Register the given extension.
pub fn register<E: Extension>(&mut self, ext: E) {
self.extensions.insert(ext.type_id(), Box::new(ext));
pub fn register<E: Extension>(
&mut self,
ext: E,
) {
let type_id = ext.type_id();
self.extensions.insert(type_id, Box::new(ext));
}
/// Register extension `ext`.
pub fn register_with_type_id(&mut self, type_id: TypeId, extension: Box<dyn Extension>) -> Result<(), Error> {
/// Register extension `extension` using the given `type_id`.
pub fn register_with_type_id(
&mut self,
type_id: TypeId,
extension: Box<dyn Extension>,
) -> Result<(), Error> {
match self.extensions.entry(type_id) {
Entry::Vacant(vacant) => { vacant.insert(extension); Ok(()) },
Entry::Vacant(vacant) => {
vacant.insert(extension);
Ok(())
},
Entry::Occupied(_) => Err(Error::ExtensionAlreadyRegistered),
}
}
@@ -140,9 +151,16 @@ impl Extensions {
self.extensions.get_mut(&ext_type_id).map(DerefMut::deref_mut).map(Extension::as_mut_any)
}
/// Deregister extension of type `E`.
pub fn deregister(&mut self, type_id: TypeId) -> Option<Box<dyn Extension>> {
self.extensions.remove(&type_id)
/// Deregister extension for the given `type_id`.
///
/// Returns `true` when the extension was registered.
pub fn deregister(&mut self, type_id: TypeId) -> bool {
self.extensions.remove(&type_id).is_some()
}
/// Returns a mutable iterator over all extensions.
pub fn iter_mut<'a>(&'a mut self) -> impl Iterator<Item = (&'a TypeId, &'a mut Box<dyn Extension>)> {
self.extensions.iter_mut()
}
}
@@ -348,10 +348,11 @@ impl sp_externalities::ExtensionStore for BasicExternalities {
}
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), sp_externalities::Error> {
self.extensions
.deregister(type_id)
.ok_or(sp_externalities::Error::ExtensionIsNotRegistered(type_id))
.map(drop)
if self.extensions.deregister(type_id) {
Ok(())
} else {
Err(sp_externalities::Error::ExtensionIsNotRegistered(type_id))
}
}
}
+11 -9
View File
@@ -19,7 +19,7 @@
use crate::{
StorageKey, StorageValue, OverlayedChanges,
backend::Backend,
backend::Backend, overlayed_changes::OverlayedExtensions,
};
use hash_db::Hasher;
use sp_core::{
@@ -27,8 +27,9 @@ use sp_core::{
hexdisplay::HexDisplay,
};
use sp_trie::{trie_types::Layout, empty_child_trie_root};
use sp_externalities::{Externalities, Extensions, Extension,
ExtensionStore};
use sp_externalities::{
Externalities, Extensions, Extension, ExtensionStore,
};
use codec::{Decode, Encode, EncodeAppend};
use sp_std::{fmt, any::{Any, TypeId}, vec::Vec, vec, boxed::Box};
@@ -115,7 +116,7 @@ pub struct Ext<'a, H, N, B>
_phantom: sp_std::marker::PhantomData<N>,
/// Extensions registered with this instance.
#[cfg(feature = "std")]
extensions: Option<&'a mut Extensions>,
extensions: Option<OverlayedExtensions<'a>>,
}
@@ -159,7 +160,7 @@ impl<'a, H, N, B> Ext<'a, H, N, B>
storage_transaction_cache,
id: rand::random(),
_phantom: Default::default(),
extensions,
extensions: extensions.map(OverlayedExtensions::new),
}
}
@@ -753,7 +754,7 @@ where
extension: Box<dyn Extension>,
) -> Result<(), sp_externalities::Error> {
if let Some(ref mut extensions) = self.extensions {
extensions.register_with_type_id(type_id, extension)
extensions.register(type_id, extension)
} else {
Err(sp_externalities::Error::ExtensionsAreNotSupported)
}
@@ -761,9 +762,10 @@ where
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), sp_externalities::Error> {
if let Some(ref mut extensions) = self.extensions {
match extensions.deregister(type_id) {
Some(_) => Ok(()),
None => Err(sp_externalities::Error::ExtensionIsNotRegistered(type_id))
if extensions.deregister(type_id) {
Ok(())
} else {
Err(sp_externalities::Error::ExtensionIsNotRegistered(type_id))
}
} else {
Err(sp_externalities::Error::ExtensionsAreNotSupported)
+58 -4
View File
@@ -31,7 +31,7 @@ mod ext;
mod testing;
#[cfg(feature = "std")]
mod basic;
mod overlayed_changes;
pub(crate) mod overlayed_changes;
#[cfg(feature = "std")]
mod proving_backend;
mod trie_backend;
@@ -907,7 +907,7 @@ mod tests {
_method: &str,
_data: &[u8],
use_native: bool,
_native_call: Option<NC>,
native_call: Option<NC>,
) -> (CallResult<R, Self::Error>, bool) {
if self.change_changes_trie_config {
ext.place_storage(
@@ -922,8 +922,15 @@ mod tests {
}
let using_native = use_native && self.native_available;
match (using_native, self.native_succeeds, self.fallback_succeeds) {
(true, true, _) | (false, _, true) => {
match (using_native, self.native_succeeds, self.fallback_succeeds, native_call) {
(true, true, _, Some(call)) => {
let res = sp_externalities::set_and_run_with_externalities(ext, || call());
(
res.map(NativeOrEncoded::Native).map_err(|_| 0),
true
)
},
(true, true, _, None) | (false, _, true, None) => {
(
Ok(
NativeOrEncoded::Encoded(
@@ -1473,4 +1480,51 @@ mod tests {
overlay.commit_transaction().unwrap();
assert_eq!(overlay.storage(b"ccc"), Some(None));
}
#[test]
fn runtime_registered_extensions_are_removed_after_execution() {
use sp_externalities::ExternalitiesExt;
sp_externalities::decl_extension! {
struct DummyExt(u32);
}
let backend = trie_backend::tests::test_trie();
let mut overlayed_changes = Default::default();
let mut offchain_overlayed_changes = Default::default();
let wasm_code = RuntimeCode::empty();
let mut state_machine = StateMachine::new(
&backend,
changes_trie::disabled_state::<_, u64>(),
&mut overlayed_changes,
&mut offchain_overlayed_changes,
&DummyCodeExecutor {
change_changes_trie_config: false,
native_available: true,
native_succeeds: true,
fallback_succeeds: false,
},
"test",
&[],
Default::default(),
&wasm_code,
TaskExecutor::new(),
);
let run_state_machine = |state_machine: &mut StateMachine<_, _, _, _>| {
state_machine.execute_using_consensus_failure_handler::<fn(_, _) -> _, _, _>(
ExecutionManager::NativeWhenPossible,
Some(|| {
sp_externalities::with_externalities(|mut ext| {
ext.register_extension(DummyExt(2)).unwrap();
}).unwrap();
Ok(())
}),
).unwrap();
};
run_state_machine(&mut state_machine);
run_state_machine(&mut state_machine);
}
}
@@ -23,7 +23,7 @@ use crate::{
backend::Backend,
stats::StateMachineStats,
};
use sp_std::vec::Vec;
use sp_std::{vec::Vec, any::{TypeId, Any}, boxed::Box};
use self::changeset::OverlayedChangeSet;
#[cfg(feature = "std")]
@@ -36,9 +36,9 @@ use crate::{
};
use crate::changes_trie::BlockNumber;
#[cfg(feature = "std")]
use std::collections::HashMap as Map;
use std::collections::{HashMap as Map, hash_map::Entry as MapEntry};
#[cfg(not(feature = "std"))]
use sp_std::collections::btree_map::BTreeMap as Map;
use sp_std::collections::btree_map::{BTreeMap as Map, Entry as MapEntry};
use sp_std::collections::btree_set::BTreeSet;
use codec::{Decode, Encode};
use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo};
@@ -46,6 +46,7 @@ use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo};
use sp_core::offchain::storage::OffchainOverlayedChanges;
use hash_db::Hasher;
use crate::DefaultError;
use sp_externalities::{Extensions, Extension};
pub use self::changeset::{OverlayedValue, NoOpenTransaction, AlreadyInRuntime, NotInRuntime};
@@ -638,7 +639,7 @@ fn retain_map<K, V, F>(map: &mut Map<K, V>, f: F)
{
map.retain(f);
}
#[cfg(not(feature = "std"))]
fn retain_map<K, V, F>(map: &mut Map<K, V>, mut f: F)
where
@@ -652,7 +653,67 @@ fn retain_map<K, V, F>(map: &mut Map<K, V>, mut f: F)
}
}
}
/// An overlayed extension is either a mutable reference
/// or an owned extension.
pub enum OverlayedExtension<'a> {
MutRef(&'a mut Box<dyn Extension>),
Owned(Box<dyn Extension>),
}
/// Overlayed extensions which are sourced from [`Extensions`].
///
/// The sourced extensions will be stored as mutable references,
/// while extensions that are registered while execution are stored
/// as owned references. After the execution of a runtime function, we
/// can safely drop this object while not having modified the original
/// list.
pub struct OverlayedExtensions<'a> {
extensions: Map<TypeId, OverlayedExtension<'a>>,
}
impl<'a> OverlayedExtensions<'a> {
/// Create a new instance of overalyed extensions from the given extensions.
pub fn new(extensions: &'a mut Extensions) -> Self {
Self {
extensions: extensions
.iter_mut()
.map(|(k, v)| (*k, OverlayedExtension::MutRef(v)))
.collect(),
}
}
/// Return a mutable reference to the requested extension.
pub fn get_mut(&mut self, ext_type_id: TypeId) -> Option<&mut dyn Any> {
self.extensions.get_mut(&ext_type_id).map(|ext| match ext {
OverlayedExtension::MutRef(ext) => ext.as_mut_any(),
OverlayedExtension::Owned(ext) => ext.as_mut_any(),
})
}
/// Register extension `extension` with the given `type_id`.
pub fn register(
&mut self,
type_id: TypeId,
extension: Box<dyn Extension>,
) -> Result<(), sp_externalities::Error> {
match self.extensions.entry(type_id) {
MapEntry::Vacant(vacant) => {
vacant.insert(OverlayedExtension::Owned(extension));
Ok(())
},
MapEntry::Occupied(_) => Err(sp_externalities::Error::ExtensionAlreadyRegistered),
}
}
/// Deregister extension with the given `type_id`.
///
/// Returns `true` when there was an extension registered for the given `type_id`.
pub fn deregister(&mut self, type_id: TypeId) -> bool {
self.extensions.remove(&type_id).is_some()
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
@@ -233,10 +233,11 @@ impl<H, N> sp_externalities::ExtensionStore for TestExternalities<H, N> where
}
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), sp_externalities::Error> {
self.extensions
.deregister(type_id)
.expect("There should be an extension we try to remove in TestExternalities");
Ok(())
if self.extensions.deregister(type_id) {
Ok(())
} else {
Err(sp_externalities::Error::ExtensionIsNotRegistered(type_id))
}
}
}