Better identifier and logging for runtime upgrades (#8123)

* A clean new attempt

* Checkpoint to move remote.

* A lot of dependency wiring to make it feature gated.

* bad macro, bad macro.

* Undo the DB mess.

* Update frame/support/src/traits.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Apply suggestions from code review

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* unbreak the build

* Better logging and ids for migrations

* Fix doc.

* Test

* Update frame/try-runtime/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Update utils/frame/try-runtime/cli/Cargo.toml

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Update frame/try-runtime/Cargo.toml

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Address most review grumbles.

* Fix build

* Add some comments

* Remove allowing one pallet at a time.

* Rework the PR

* nit

* Slightly better error handling.

* Remove files

* Update utils/frame/remote-externalities/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Update frame/support/src/dispatch.rs

* Update frame/support/src/dispatch.rs

* Fix test

* Make extension trait.

* Bring back try-runtime/std

* remove bincode

* Remove warning

* Change test features

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Kian Paimani
2021-02-26 15:41:23 +00:00
committed by GitHub
parent 2bd41a0251
commit 33b435894a
11 changed files with 194 additions and 108 deletions
+2 -2
View File
@@ -6436,12 +6436,12 @@ name = "remote-externalities"
version = "0.9.0"
dependencies = [
"async-std",
"bincode",
"env_logger 0.8.2",
"futures 0.1.30",
"futures 0.3.12",
"hex-literal",
"jsonrpc-core-client",
"log",
"parity-scale-codec",
"sc-rpc",
"sc-rpc-api",
"sp-core",
+1 -1
View File
@@ -159,7 +159,7 @@ pub fn run() -> Result<()> {
let task_manager = sc_service::TaskManager::new(
config.task_executor.clone(),
registry,
).unwrap();
).map_err(|e| sc_cli::Error::Service(sc_service::Error::Prometheus(e)))?;
Ok((cmd.run::<Block, Executor>(config), task_manager))
})
@@ -17,7 +17,7 @@
//! Proc macro of Support code for the runtime.
#![recursion_limit="512"]
#![recursion_limit = "512"]
mod storage;
mod construct_runtime;
+72 -38
View File
@@ -80,18 +80,18 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// // FRAME pallets.
/// #[weight = 0]
/// fn my_function(origin, var: u64) -> dispatch::DispatchResult {
/// // Your implementation
/// Ok(())
/// // Your implementation
/// Ok(())
/// }
///
/// // Public functions are both dispatchable and available to other
/// // Public functions are both dispatchable and available to other
/// // FRAME pallets.
/// #[weight = 0]
/// pub fn my_public_function(origin) -> dispatch::DispatchResult {
/// pub fn my_public_function(origin) -> dispatch::DispatchResult {
/// // Your implementation
/// Ok(())
/// Ok(())
/// }
/// }
/// }
/// }
/// # fn main() {}
/// ```
@@ -99,8 +99,10 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// The declaration is set with the header where:
///
/// * `Module`: The struct generated by the macro, with type `Config`.
/// * `Call`: The enum generated for every pallet, which implements [`Callable`](./dispatch/trait.Callable.html).
/// * `origin`: Alias of `T::Origin`, declared by the [`impl_outer_origin!`](./macro.impl_outer_origin.html) macro.
/// * `Call`: The enum generated for every pallet, which implements
/// [`Callable`](./dispatch/trait.Callable.html).
/// * `origin`: Alias of `T::Origin`, declared by the
/// [`impl_outer_origin!`](./macro.impl_outer_origin.html) macro.
/// * `Result`: The expected return type from pallet functions.
///
/// The first parameter of dispatchable functions must always be `origin`.
@@ -119,15 +121,15 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// pub struct Module<T: Config> for enum Call where origin: T::Origin {
/// #[weight = 0]
/// fn my_long_function(origin) -> dispatch::DispatchResult {
/// // Your implementation
/// // Your implementation
/// Ok(())
/// }
///
/// #[weight = 0]
/// fn my_short_function(origin) {
/// // Your implementation
/// // Your implementation
/// }
/// }
/// }
/// }
/// # fn main() {}
/// ```
@@ -184,7 +186,7 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// #[weight = 0]
/// #[transactional]
/// fn my_short_function(origin) {
/// // Your implementation
/// // Your implementation
/// }
/// }
/// }
@@ -203,12 +205,12 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// decl_module! {
/// pub struct Module<T: Config> for enum Call where origin: T::Origin {
/// #[weight = 0]
/// fn my_privileged_function(origin) -> dispatch::DispatchResult {
/// fn my_privileged_function(origin) -> dispatch::DispatchResult {
/// ensure_root(origin)?;
/// // Your implementation
/// // Your implementation
/// Ok(())
/// }
/// }
/// }
/// }
/// # fn main() {}
/// ```
@@ -218,15 +220,17 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// Attributes on functions are supported, but must be in the order of:
/// 1. Optional #\[doc\] attribute.
/// 2. #\[weight\] attribute.
/// 3. Optional function attributes, for instance #\[transactional\]. Those function attributes will be written
/// only on the dispatchable functions implemented on `Module`, not on the `Call` enum variant.
/// 3. Optional function attributes, for instance #\[transactional\]. Those function attributes will
/// be written only on the dispatchable functions implemented on `Module`, not on the `Call` enum
/// variant.
///
/// ## Multiple Module Instances Example
///
/// A Substrate module can be built such that multiple instances of the same module can be used within a single
/// runtime. For example, the [Balances module](../pallet_balances/index.html) can be added multiple times to your
/// runtime in order to support multiple, independent currencies for your blockchain. Here is an example of how
/// you would declare such a module using the `decl_module!` macro:
/// A Substrate module can be built such that multiple instances of the same module can be used
/// within a single runtime. For example, the [Balances module](../pallet_balances/index.html) can
/// be added multiple times to your runtime in order to support multiple, independent currencies for
/// your blockchain. Here is an example of how you would declare such a module using the
/// `decl_module!` macro:
///
/// ```
/// # #[macro_use]
@@ -251,10 +255,10 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
///
/// ## Where clause
///
/// Besides the default `origin: T::Origin`, you can also pass other bounds to the module declaration.
/// This where bound will be replicated to all types generated by this macro. The chaining of multiple
/// trait bounds with `+` is not supported. If multiple bounds for one type are required, it needs to
/// be split up into multiple bounds.
/// Besides the default `origin: T::Origin`, you can also pass other bounds to the module
/// declaration. This where bound will be replicated to all types generated by this macro. The
/// chaining of multiple trait bounds with `+` is not supported. If multiple bounds for one type are
/// required, it needs to be split up into multiple bounds.
///
/// ```
/// # #[macro_use]
@@ -276,16 +280,18 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// The following are reserved function signatures:
///
/// * `deposit_event`: Helper function for depositing an [event](https://docs.substrate.dev/docs/event-enum).
/// The default behavior is to call `deposit_event` from the [System module](../frame_system/index.html).
/// However, you can write your own implementation for events in your runtime. To use the default behavior,
/// add `fn deposit_event() = default;` to your `Module`.
/// The default behavior is to call `deposit_event` from the [System
/// module](../frame_system/index.html). However, you can write your own implementation for events
/// in your runtime. To use the default behavior, add `fn deposit_event() = default;` to your
/// `Module`.
///
/// The following reserved functions also take the block number (with type `T::BlockNumber`) as an optional input:
/// The following reserved functions also take the block number (with type `T::BlockNumber`) as an
/// optional input:
///
/// * `on_runtime_upgrade`: Executes at the beginning of a block prior to on_initialize when there
/// is a runtime upgrade. This allows each module to upgrade its storage before the storage items are used.
/// As such, **calling other modules must be avoided**!! Using this function will implement the
/// [`OnRuntimeUpgrade`](../sp_runtime/traits/trait.OnRuntimeUpgrade.html) trait.
/// is a runtime upgrade. This allows each module to upgrade its storage before the storage items
/// are used. As such, **calling other modules must be avoided**!! Using this function will
/// implement the [`OnRuntimeUpgrade`](../sp_runtime/traits/trait.OnRuntimeUpgrade.html) trait.
/// Function signature must be `fn on_runtime_upgrade() -> frame_support::weights::Weight`.
///
/// * `on_initialize`: Executes at the beginning of a block. Using this function will
@@ -300,11 +306,11 @@ impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
/// * `fn on_finalize(n: BlockNumber) -> frame_support::weights::Weight` or
/// * `fn on_finalize() -> frame_support::weights::Weight`
///
/// * `offchain_worker`: Executes at the beginning of a block and produces extrinsics for a future block
/// upon completion. Using this function will implement the
/// * `offchain_worker`: Executes at the beginning of a block and produces extrinsics for a future
/// block upon completion. Using this function will implement the
/// [`OffchainWorker`](./traits/trait.OffchainWorker.html) trait.
/// * `integrity_test`: Executes in a test generated by `construct_runtime`, note it doesn't
/// execute in an externalities-provided environment. Implement
/// * `integrity_test`: Executes in a test generated by `construct_runtime`, note it doesn't execute
/// in an externalities-provided environment. Implement
/// [`IntegrityTest`](./trait.IntegrityTest.html) trait.
#[macro_export]
macro_rules! decl_module {
@@ -1325,13 +1331,27 @@ macro_rules! decl_module {
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_runtime_upgrade"));
let result: $return = (|| { $( $impl )* })();
$crate::crate_to_pallet_version!()
let new_storage_version = $crate::crate_to_pallet_version!();
new_storage_version
.put_into_storage::<<$trait_instance as $system::Config>::PalletInfo, Self>();
let additional_write = <
<$trait_instance as $system::Config>::DbWeight as $crate::traits::Get<_>
>::get().writes(1);
let pallet_name = <<
$trait_instance
as
$system::Config
>::PalletInfo as $crate::traits::PalletInfo>::name::<Self>().expect("pallet will have name in the runtime; qed");
$crate::debug::info!(
target: $crate::LOG_TARGET,
"⚠️ running migration for {} and setting new storage version to {:?}",
pallet_name,
new_storage_version,
);
result.saturating_add(additional_write)
}
@@ -1359,9 +1379,23 @@ macro_rules! decl_module {
fn on_runtime_upgrade() -> $crate::dispatch::Weight {
$crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_runtime_upgrade"));
$crate::crate_to_pallet_version!()
let new_storage_version = $crate::crate_to_pallet_version!();
new_storage_version
.put_into_storage::<<$trait_instance as $system::Config>::PalletInfo, Self>();
let pallet_name = <<
$trait_instance
as
$system::Config
>::PalletInfo as $crate::traits::PalletInfo>::name::<Self>().expect("pallet will have name in the runtime; qed");
$crate::debug::info!(
target: $crate::LOG_TARGET,
"✅ no migration for '{}' and setting new storage version to {:?}",
pallet_name,
new_storage_version,
);
<
<$trait_instance as $system::Config>::DbWeight as $crate::traits::Get<_>
>::get().writes(1)
+3
View File
@@ -80,6 +80,9 @@ pub use self::storage::{
pub use self::dispatch::{Parameter, Callable};
pub use sp_runtime::{self, ConsensusEngineId, print, traits::Printable};
/// A unified log target for support operations.
pub const LOG_TARGET: &'static str = "runtime::frame-support";
/// A type that cannot be instantiated.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Never {}
+48
View File
@@ -1553,6 +1553,54 @@ pub trait OnGenesis {
fn on_genesis() {}
}
/// Prefix to be used (optionally) for implementing [`OnRuntimeUpgrade::storage_key`].
#[cfg(feature = "try-runtime")]
pub const ON_RUNTIME_UPGRADE_PREFIX: &[u8] = b"__ON_RUNTIME_UPGRADE__";
/// Some helper functions for [`OnRuntimeUpgrade`] during `try-runtime` testing.
#[cfg(feature = "try-runtime")]
pub trait OnRuntimeUpgradeHelpersExt {
/// Generate a storage key unique to this runtime upgrade.
///
/// This can be used to communicate data from pre-upgrade to post-upgrade state and check
/// them. See [`set_temp_storage`] and [`get_temp_storage`].
#[cfg(feature = "try-runtime")]
fn storage_key(ident: &str) -> [u8; 32] {
let prefix = sp_io::hashing::twox_128(ON_RUNTIME_UPGRADE_PREFIX);
let ident = sp_io::hashing::twox_128(ident.as_bytes());
let mut final_key = [0u8; 32];
final_key[..16].copy_from_slice(&prefix);
final_key[16..].copy_from_slice(&ident);
final_key
}
/// Get temporary storage data written by [`set_temp_storage`].
///
/// Returns `None` if either the data is unavailable or un-decodable.
///
/// A `at` storage identifier must be provided to indicate where the storage is being read from.
#[cfg(feature = "try-runtime")]
fn get_temp_storage<T: Decode>(at: &str) -> Option<T> {
sp_io::storage::get(&Self::storage_key(at))
.and_then(|bytes| Decode::decode(&mut &*bytes).ok())
}
/// Write some temporary data to a specific storage that can be read (potentially in
/// post-upgrade hook) via [`get_temp_storage`].
///
/// A `at` storage identifier must be provided to indicate where the storage is being written
/// to.
#[cfg(feature = "try-runtime")]
fn set_temp_storage<T: Encode>(data: T, at: &str) {
sp_io::storage::set(&Self::storage_key(at), &data.encode());
}
}
#[cfg(feature = "try-runtime")]
impl<U: OnRuntimeUpgrade> OnRuntimeUpgradeHelpersExt for U {}
/// The runtime upgrade trait.
///
/// Implementing this lets you express what should happen when the runtime upgrades,
+2 -2
View File
@@ -29,7 +29,7 @@ use codec::{Encode, Decode};
/// Storage key.
#[derive(PartialEq, Eq, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone))]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone, Encode, Decode))]
pub struct StorageKey(
#[cfg_attr(feature = "std", serde(with = "impl_serde::serialize"))] pub Vec<u8>,
);
@@ -107,7 +107,7 @@ impl PrefixedStorageKey {
/// Storage data associated to a [`StorageKey`].
#[derive(PartialEq, Eq, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone))]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, PartialOrd, Ord, Clone, Encode, Decode))]
pub struct StorageData(
#[cfg_attr(feature = "std", serde(with="impl_serde::serialize"))]
pub Vec<u8>,
@@ -16,12 +16,12 @@ targets = ["x86_64-unknown-linux-gnu"]
jsonrpc-core-client = { version = "15.1.0", features = ["http"] }
sc-rpc-api = { version = "0.9.0", path = "../../../client/rpc-api" }
sc-rpc = { version = "3.0.0", path = "../../../client/rpc" }
futures = "0.1.29"
futures = "0.3"
hex-literal = "0.3.1"
env_logger = "0.8.2"
log = "0.4.11"
bincode = "1.3.1"
codec = { package = "parity-scale-codec", version = "2.0.0" }
tokio = "0.1.22"
sp-io = { version = "3.0.0", path = "../../../primitives/io" }
@@ -112,7 +112,11 @@ use sp_core::{
hexdisplay::HexDisplay,
storage::{StorageKey, StorageData},
};
use futures::future::Future;
use futures::{
compat::Future01CompatExt,
TryFutureExt,
};
use codec::{Encode, Decode};
type KeyPair = (StorageKey, StorageData);
type Number = u32;
@@ -192,7 +196,6 @@ impl CacheConfig {
pub struct Builder {
inject: Vec<KeyPair>,
mode: Mode,
chain: String,
}
impl Default for Builder {
@@ -205,7 +208,6 @@ impl Default for Builder {
cache: None,
modules: Default::default(),
}),
chain: "UNSET".into(),
}
}
}
@@ -229,74 +231,71 @@ impl Builder {
// RPC methods
impl Builder {
async fn rpc_get_head(&self) -> Hash {
let mut rt = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
async fn rpc_get_head(&self) -> Result<Hash, &'static str> {
let uri = self.as_online().uri.clone();
rt.block_on::<_, _, ()>(futures::lazy(move || {
trace!(target: LOG_TARGET, "rpc: finalized_head");
let client: sc_rpc_api::chain::ChainClient<Number, Hash, (), ()> =
jsonrpc_core_client::transports::http::connect(&uri).wait().unwrap();
Ok(client.finalized_head().wait().unwrap())
}))
.unwrap()
trace!(target: LOG_TARGET, "rpc: finalized_head");
let client: sc_rpc_api::chain::ChainClient<Number, Hash, (), ()> =
jsonrpc_core_client::transports::http::connect(&uri)
.compat()
.map_err(|_| "client initialization failed")
.await?;
client.finalized_head().compat().map_err(|_| "rpc finalized_head failed.").await
}
/// Relay the request to `state_getPairs` rpc endpoint.
///
/// Note that this is an unsafe RPC.
async fn rpc_get_pairs(&self, prefix: StorageKey, at: Hash) -> Vec<KeyPair> {
let mut rt = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
async fn rpc_get_pairs(
&self,
prefix: StorageKey,
at: Hash,
) -> Result<Vec<KeyPair>, &'static str> {
let uri = self.as_online().uri.clone();
rt.block_on::<_, _, ()>(futures::lazy(move || {
trace!(target: LOG_TARGET, "rpc: storage_pairs: {:?} / {:?}", prefix, at);
let client: sc_rpc_api::state::StateClient<Hash> =
jsonrpc_core_client::transports::http::connect(&uri).wait().unwrap();
Ok(client.storage_pairs(prefix, Some(at)).wait().unwrap())
}))
.unwrap()
}
/// Get the chain name.
async fn chain_name(&self) -> String {
let mut rt = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
let uri = self.as_online().uri.clone();
rt.block_on::<_, _, ()>(futures::lazy(move || {
trace!(target: LOG_TARGET, "rpc: system_chain");
let client: sc_rpc_api::system::SystemClient<(), ()> =
jsonrpc_core_client::transports::http::connect(&uri).wait().unwrap();
Ok(client.system_chain().wait().unwrap())
}))
.unwrap()
trace!(target: LOG_TARGET, "rpc: storage_pairs: {:?} / {:?}", prefix, at);
let client: sc_rpc_api::state::StateClient<Hash> =
jsonrpc_core_client::transports::http::connect(&uri)
.compat()
.map_err(|_| "client initialization failed")
.await?;
client
.storage_pairs(prefix, Some(at))
.compat()
.map_err(|_| "rpc finalized_head failed.")
.await
}
}
// Internal methods
impl Builder {
/// Save the given data as cache.
fn save_cache(&self, data: &[KeyPair], path: &Path) {
let bdata = bincode::serialize(data).unwrap();
fn save_cache(&self, data: &[KeyPair], path: &Path) -> Result<(), &'static str> {
info!(target: LOG_TARGET, "writing to cache file {:?}", path);
fs::write(path, bdata).unwrap();
fs::write(path, data.encode()).map_err(|_| "fs::write failed.")?;
Ok(())
}
/// initialize `Self` from cache. Panics if the file does not exist.
fn load_cache(&self, path: &Path) -> Vec<KeyPair> {
fn load_cache(&self, path: &Path) -> Result<Vec<KeyPair>, &'static str> {
info!(target: LOG_TARGET, "scraping keypairs from cache {:?}", path,);
let bytes = fs::read(path).unwrap();
bincode::deserialize(&bytes[..]).unwrap()
let bytes = fs::read(path).map_err(|_| "fs::read failed.")?;
Decode::decode(&mut &*bytes).map_err(|_| "decode failed")
}
/// Build `Self` from a network node denoted by `uri`.
async fn load_remote(&self) -> Vec<KeyPair> {
async fn load_remote(&self) -> Result<Vec<KeyPair>, &'static str> {
let config = self.as_online();
let at = self.as_online().at.unwrap().clone();
let at = self
.as_online()
.at
.expect("online config must be initialized by this point; qed.")
.clone();
info!(target: LOG_TARGET, "scraping keypairs from remote node {} @ {:?}", config.uri, at);
let keys_and_values = if config.modules.len() > 0 {
let mut filtered_kv = vec![];
for f in config.modules.iter() {
let hashed_prefix = StorageKey(twox_128(f.as_bytes()).to_vec());
let module_kv = self.rpc_get_pairs(hashed_prefix.clone(), at).await;
let module_kv = self.rpc_get_pairs(hashed_prefix.clone(), at).await?;
info!(
target: LOG_TARGET,
"downloaded data for module {} (count: {} / prefix: {:?}).",
@@ -309,25 +308,26 @@ impl Builder {
filtered_kv
} else {
info!(target: LOG_TARGET, "downloading data for all modules.");
self.rpc_get_pairs(StorageKey(vec![]), at).await.into_iter().collect::<Vec<_>>()
self.rpc_get_pairs(StorageKey(vec![]), at).await?.into_iter().collect::<Vec<_>>()
};
keys_and_values
Ok(keys_and_values)
}
async fn init_remote_client(&mut self) {
self.as_online_mut().at = Some(self.rpc_get_head().await);
self.chain = self.chain_name().await;
async fn init_remote_client(&mut self) -> Result<(), &'static str> {
let at = self.rpc_get_head().await?;
self.as_online_mut().at = Some(at);
Ok(())
}
async fn pre_build(mut self) -> Vec<KeyPair> {
async fn pre_build(mut self) -> Result<Vec<KeyPair>, &'static str> {
let mut base_kv = match self.mode.clone() {
Mode::Offline(config) => self.load_cache(&config.cache.path()),
Mode::Offline(config) => self.load_cache(&config.cache.path())?,
Mode::Online(config) => {
self.init_remote_client().await;
let kp = self.load_remote().await;
self.init_remote_client().await?;
let kp = self.load_remote().await?;
if let Some(c) = config.cache {
self.save_cache(&kp, &c.path());
self.save_cache(&kp, &c.path())?;
}
kp
}
@@ -339,7 +339,7 @@ impl Builder {
self.inject.len()
);
base_kv.extend(self.inject.clone());
base_kv
Ok(base_kv)
}
}
@@ -365,8 +365,8 @@ impl Builder {
}
/// Build the test externalities.
pub async fn build(self) -> TestExternalities {
let kv = self.pre_build().await;
pub async fn build(self) -> Result<TestExternalities, &'static str> {
let kv = self.pre_build().await?;
let mut ext = TestExternalities::new_empty();
info!(target: LOG_TARGET, "injecting a total of {} keys", kv.len());
@@ -374,10 +374,11 @@ impl Builder {
let (k, v) = (k.0, v.0);
ext.insert(k, v);
}
ext
Ok(ext)
}
}
#[cfg(feature = "remote-test")]
#[cfg(test)]
mod tests {
use super::*;
@@ -390,7 +391,6 @@ mod tests {
}
#[async_std::test]
#[cfg(feature = "remote-test")]
async fn can_build_one_pallet() {
init_logger();
Builder::new()
@@ -400,6 +400,7 @@ mod tests {
}))
.build()
.await
.unwrap()
.execute_with(|| {});
}
@@ -412,11 +413,11 @@ mod tests {
}))
.build()
.await
.unwrap()
.execute_with(|| {});
}
#[async_std::test]
#[cfg(feature = "remote-test")]
async fn can_create_cache() {
init_logger();
Builder::new()
@@ -429,6 +430,7 @@ mod tests {
}))
.build()
.await
.unwrap()
.execute_with(|| {});
let to_delete = std::fs::read_dir(CacheConfig::default().directory)
@@ -446,9 +448,8 @@ mod tests {
}
#[async_std::test]
#[cfg(feature = "remote-test")]
async fn can_build_all() {
init_logger();
Builder::new().build().await.execute_with(|| {});
Builder::new().build().await.unwrap().execute_with(|| {});
}
}
@@ -133,7 +133,7 @@ impl TryRuntimeCmd {
};
// inject the code into this ext.
builder.inject(&[(code_key, code)]).build().await
builder.inject(&[(code_key, code)]).build().await?
};
let encoded_result = StateMachine::<_, _, NumberFor<B>, _>::new(