mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 13:31:10 +00:00
Introduce code_substitute (#8898)
This introduces a new field `code_substitute` into the chain spec. This can be used to substitute the on-chain wasm starting from a given block until there is another wasm on chain (determined through the `spec_version`). This can be used to fix broken on chain wasm runtimes.
This commit is contained in:
@@ -32,7 +32,7 @@ pub type Backend = sc_client_db::Backend<node_primitives::Block>;
|
||||
/// Test client type.
|
||||
pub type Client = client::Client<
|
||||
Backend,
|
||||
client::LocalCallExecutor<Backend, Executor>,
|
||||
client::LocalCallExecutor<node_primitives::Block, Backend, Executor>,
|
||||
node_primitives::Block,
|
||||
node_runtime::RuntimeApi,
|
||||
>;
|
||||
@@ -63,7 +63,7 @@ pub trait TestClientBuilderExt: Sized {
|
||||
|
||||
impl TestClientBuilderExt for substrate_test_client::TestClientBuilder<
|
||||
node_primitives::Block,
|
||||
client::LocalCallExecutor<Backend, Executor>,
|
||||
client::LocalCallExecutor<node_primitives::Block, Backend, Executor>,
|
||||
Backend,
|
||||
GenesisParameters,
|
||||
> {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
use std::{borrow::Cow, fs::File, path::PathBuf, sync::Arc, collections::HashMap};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sp_core::storage::{StorageKey, StorageData, ChildInfo, Storage, StorageChild};
|
||||
use sp_core::{storage::{StorageKey, StorageData, ChildInfo, Storage, StorageChild}, Bytes};
|
||||
use sp_runtime::BuildStorage;
|
||||
use serde_json as json;
|
||||
use crate::{RuntimeGenesis, ChainType, extension::GetExtension, Properties};
|
||||
@@ -160,6 +160,12 @@ struct ClientSpec<E> {
|
||||
#[serde(skip_serializing)]
|
||||
genesis: serde::de::IgnoredAny,
|
||||
light_sync_state: Option<SerializableLightSyncState>,
|
||||
/// Mapping from `block_hash` to `wasm_code`.
|
||||
///
|
||||
/// The given `wasm_code` will be used to substitute the on-chain wasm code from the given
|
||||
/// block hash onwards.
|
||||
#[serde(default)]
|
||||
code_substitutes: HashMap<String, Bytes>,
|
||||
}
|
||||
|
||||
/// A type denoting empty extensions.
|
||||
@@ -249,6 +255,7 @@ impl<G, E> ChainSpec<G, E> {
|
||||
consensus_engine: (),
|
||||
genesis: Default::default(),
|
||||
light_sync_state: None,
|
||||
code_substitutes: HashMap::new(),
|
||||
};
|
||||
|
||||
ChainSpec {
|
||||
@@ -395,6 +402,10 @@ where
|
||||
fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState) {
|
||||
ChainSpec::set_light_sync_state(self, light_sync_state)
|
||||
}
|
||||
|
||||
fn code_substitutes(&self) -> std::collections::HashMap<String, Vec<u8>> {
|
||||
self.client_spec.code_substitutes.iter().map(|(h, c)| (h.clone(), c.0.clone())).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardcoded infomation that allows light clients to sync quickly.
|
||||
|
||||
@@ -161,6 +161,8 @@ pub trait ChainSpec: BuildStorage + Send + Sync {
|
||||
fn set_storage(&mut self, storage: Storage);
|
||||
/// Hardcode infomation to allow light clients to sync quickly into the chain spec.
|
||||
fn set_light_sync_state(&mut self, light_sync_state: SerializableLightSyncState);
|
||||
/// Returns code substitutes that should be used for the on chain wasm.
|
||||
fn code_substitutes(&self) -> std::collections::HashMap<String, Vec<u8>>;
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for dyn ChainSpec {
|
||||
|
||||
@@ -50,7 +50,7 @@ use sp_runtime::traits::{
|
||||
};
|
||||
use sp_api::{ProvideRuntimeApi, CallApiAt};
|
||||
use sc_executor::{NativeExecutor, NativeExecutionDispatch, RuntimeInfo};
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, str::FromStr};
|
||||
use wasm_timer::SystemTime;
|
||||
use sc_telemetry::{
|
||||
telemetry,
|
||||
@@ -150,6 +150,7 @@ pub type TFullBackend<TBl> = sc_client_db::Backend<TBl>;
|
||||
|
||||
/// Full client call executor type.
|
||||
pub type TFullCallExecutor<TBl, TExecDisp> = crate::client::LocalCallExecutor<
|
||||
TBl,
|
||||
sc_client_db::Backend<TBl>,
|
||||
NativeExecutor<TExecDisp>,
|
||||
>;
|
||||
@@ -172,6 +173,7 @@ pub type TLightCallExecutor<TBl, TExecDisp> = sc_light::GenesisCallExecutor<
|
||||
HashFor<TBl>
|
||||
>,
|
||||
crate::client::LocalCallExecutor<
|
||||
TBl,
|
||||
sc_light::Backend<
|
||||
sc_client_db::light::LightStorage<TBl>,
|
||||
HashFor<TBl>
|
||||
@@ -206,7 +208,7 @@ pub type TLightClientWithBackend<TBl, TRtApi, TExecDisp, TBackend> = Client<
|
||||
TBackend,
|
||||
sc_light::GenesisCallExecutor<
|
||||
TBackend,
|
||||
crate::client::LocalCallExecutor<TBackend, NativeExecutor<TExecDisp>>,
|
||||
crate::client::LocalCallExecutor<TBl, TBackend, NativeExecutor<TExecDisp>>,
|
||||
>,
|
||||
TBl,
|
||||
TRtApi,
|
||||
@@ -295,6 +297,7 @@ pub fn new_full_client<TBl, TRtApi, TExecDisp>(
|
||||
) -> Result<TFullClient<TBl, TRtApi, TExecDisp>, Error> where
|
||||
TBl: BlockT,
|
||||
TExecDisp: NativeExecutionDispatch + 'static,
|
||||
TBl::Hash: FromStr,
|
||||
{
|
||||
new_full_parts(config, telemetry).map(|parts| parts.0)
|
||||
}
|
||||
@@ -303,9 +306,10 @@ pub fn new_full_client<TBl, TRtApi, TExecDisp>(
|
||||
pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
|
||||
config: &Configuration,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
) -> Result<TFullParts<TBl, TRtApi, TExecDisp>, Error> where
|
||||
) -> Result<TFullParts<TBl, TRtApi, TExecDisp>, Error> where
|
||||
TBl: BlockT,
|
||||
TExecDisp: NativeExecutionDispatch + 'static,
|
||||
TBl::Hash: FromStr,
|
||||
{
|
||||
let keystore_container = KeystoreContainer::new(&config.keystore)?;
|
||||
|
||||
@@ -349,6 +353,16 @@ pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
|
||||
sc_offchain::OffchainDb::factory_from_backend(&*backend),
|
||||
);
|
||||
|
||||
let wasm_runtime_substitutes = config.chain_spec.code_substitutes().into_iter().map(|(h, c)| {
|
||||
let hash = TBl::Hash::from_str(&h)
|
||||
.map_err(|_|
|
||||
Error::Application(Box::from(
|
||||
format!("Failed to parse `{}` as block hash for code substitutes.", h)
|
||||
))
|
||||
)?;
|
||||
Ok((hash, c))
|
||||
}).collect::<Result<std::collections::HashMap<_, _>, Error>>()?;
|
||||
|
||||
let client = new_client(
|
||||
backend.clone(),
|
||||
executor,
|
||||
@@ -363,6 +377,7 @@ pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
|
||||
offchain_worker_enabled : config.offchain_worker.enabled,
|
||||
offchain_indexing_api: config.offchain_worker.indexing_enabled,
|
||||
wasm_runtime_overrides: config.wasm_runtime_overrides.clone(),
|
||||
wasm_runtime_substitutes,
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -453,11 +468,11 @@ pub fn new_client<E, Block, RA>(
|
||||
spawn_handle: Box<dyn SpawnNamed>,
|
||||
prometheus_registry: Option<Registry>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
config: ClientConfig,
|
||||
config: ClientConfig<Block>,
|
||||
) -> Result<
|
||||
crate::client::Client<
|
||||
Backend<Block>,
|
||||
crate::client::LocalCallExecutor<Backend<Block>, E>,
|
||||
crate::client::LocalCallExecutor<Block, Backend<Block>, E>,
|
||||
Block,
|
||||
RA,
|
||||
>,
|
||||
|
||||
@@ -32,47 +32,56 @@ use sp_core::{
|
||||
};
|
||||
use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache};
|
||||
use sc_client_api::{backend, call_executor::CallExecutor};
|
||||
use super::{client::ClientConfig, wasm_override::WasmOverride};
|
||||
use super::{client::ClientConfig, wasm_override::WasmOverride, wasm_substitutes::WasmSubstitutes};
|
||||
|
||||
/// Call executor that executes methods locally, querying all required
|
||||
/// data from local backend.
|
||||
pub struct LocalCallExecutor<B, E> {
|
||||
pub struct LocalCallExecutor<Block: BlockT, B, E> {
|
||||
backend: Arc<B>,
|
||||
executor: E,
|
||||
wasm_override: Option<WasmOverride<E>>,
|
||||
wasm_substitutes: WasmSubstitutes<Block, E, B>,
|
||||
spawn_handle: Box<dyn SpawnNamed>,
|
||||
client_config: ClientConfig,
|
||||
client_config: ClientConfig<Block>,
|
||||
}
|
||||
|
||||
impl<B, E> LocalCallExecutor<B, E>
|
||||
impl<Block: BlockT, B, E> LocalCallExecutor<Block, B, E>
|
||||
where
|
||||
E: CodeExecutor + RuntimeInfo + Clone + 'static
|
||||
E: CodeExecutor + RuntimeInfo + Clone + 'static,
|
||||
B: backend::Backend<Block>,
|
||||
{
|
||||
/// Creates new instance of local call executor.
|
||||
pub fn new(
|
||||
backend: Arc<B>,
|
||||
executor: E,
|
||||
spawn_handle: Box<dyn SpawnNamed>,
|
||||
client_config: ClientConfig,
|
||||
client_config: ClientConfig<Block>,
|
||||
) -> sp_blockchain::Result<Self> {
|
||||
let wasm_override = client_config.wasm_runtime_overrides
|
||||
.as_ref()
|
||||
.map(|p| WasmOverride::new(p.clone(), executor.clone()))
|
||||
.transpose()?;
|
||||
|
||||
let wasm_substitutes = WasmSubstitutes::new(
|
||||
client_config.wasm_runtime_substitutes.clone(),
|
||||
executor.clone(),
|
||||
backend.clone(),
|
||||
)?;
|
||||
|
||||
Ok(LocalCallExecutor {
|
||||
backend,
|
||||
executor,
|
||||
wasm_override,
|
||||
spawn_handle,
|
||||
client_config,
|
||||
wasm_substitutes,
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if local runtime code overrides are enabled and one is available
|
||||
/// for the given `BlockId`. If yes, return it; otherwise return the same
|
||||
/// `RuntimeCode` instance that was passed.
|
||||
fn check_override<'a, Block>(
|
||||
fn check_override<'a>(
|
||||
&'a self,
|
||||
onchain_code: RuntimeCode<'a>,
|
||||
id: &BlockId<Block>,
|
||||
@@ -81,16 +90,16 @@ where
|
||||
Block: BlockT,
|
||||
B: backend::Backend<Block>,
|
||||
{
|
||||
let spec = self.runtime_version(id)?.spec_version;
|
||||
let code = if let Some(d) = self.wasm_override
|
||||
.as_ref()
|
||||
.map::<sp_blockchain::Result<Option<RuntimeCode>>, _>(|o| {
|
||||
let spec = self.runtime_version(id)?.spec_version;
|
||||
Ok(o.get(&spec, onchain_code.heap_pages))
|
||||
})
|
||||
.transpose()?
|
||||
.map(|o| o.get(&spec, onchain_code.heap_pages))
|
||||
.flatten() {
|
||||
log::debug!(target: "wasm_overrides", "using WASM override for block {}", id);
|
||||
d
|
||||
} else if let Some(s) = self.wasm_substitutes.get(spec, onchain_code.heap_pages, id) {
|
||||
log::debug!(target: "wasm_substitutes", "Using WASM substitute for block {:?}", id);
|
||||
s
|
||||
} else {
|
||||
log::debug!(
|
||||
target: "wasm_overrides",
|
||||
@@ -104,7 +113,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> Clone for LocalCallExecutor<B, E> where E: Clone {
|
||||
impl<Block: BlockT, B, E> Clone for LocalCallExecutor<Block, B, E> where E: Clone {
|
||||
fn clone(&self) -> Self {
|
||||
LocalCallExecutor {
|
||||
backend: self.backend.clone(),
|
||||
@@ -112,11 +121,12 @@ impl<B, E> Clone for LocalCallExecutor<B, E> where E: Clone {
|
||||
wasm_override: self.wasm_override.clone(),
|
||||
spawn_handle: self.spawn_handle.clone(),
|
||||
client_config: self.client_config.clone(),
|
||||
wasm_substitutes: self.wasm_substitutes.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block> CallExecutor<Block> for LocalCallExecutor<B, E>
|
||||
impl<B, E, Block> CallExecutor<Block> for LocalCallExecutor<Block, B, E>
|
||||
where
|
||||
B: backend::Backend<Block>,
|
||||
E: CodeExecutor + RuntimeInfo + Clone + 'static,
|
||||
@@ -314,7 +324,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block> sp_version::GetRuntimeVersion<Block> for LocalCallExecutor<B, E>
|
||||
impl<Block, B, E> sp_version::GetRuntimeVersion<Block> for LocalCallExecutor<Block, B, E>
|
||||
where
|
||||
B: backend::Backend<Block>,
|
||||
E: CodeExecutor + RuntimeInfo + Clone + 'static,
|
||||
@@ -357,11 +367,7 @@ mod tests {
|
||||
|
||||
// wasm_runtime_overrides is `None` here because we construct the
|
||||
// LocalCallExecutor directly later on
|
||||
let client_config = ClientConfig {
|
||||
offchain_worker_enabled: false,
|
||||
offchain_indexing_api: false,
|
||||
wasm_runtime_overrides: None,
|
||||
};
|
||||
let client_config = ClientConfig::default();
|
||||
|
||||
// client is used for the convenience of creating and inserting the genesis block.
|
||||
let _client = substrate_test_runtime_client::client::new_with_backend::<
|
||||
@@ -383,10 +389,15 @@ mod tests {
|
||||
|
||||
let call_executor = LocalCallExecutor {
|
||||
backend: backend.clone(),
|
||||
executor,
|
||||
executor: executor.clone(),
|
||||
wasm_override: Some(overrides),
|
||||
spawn_handle: Box::new(TaskExecutor::new()),
|
||||
client_config,
|
||||
wasm_substitutes: WasmSubstitutes::new(
|
||||
Default::default(),
|
||||
executor.clone(),
|
||||
backend.clone(),
|
||||
).unwrap(),
|
||||
};
|
||||
|
||||
let check = call_executor.check_override(onchain_code, &BlockId::Number(Default::default()))
|
||||
|
||||
@@ -118,7 +118,7 @@ pub struct Client<B, E, Block, RA> where Block: BlockT {
|
||||
importing_block: RwLock<Option<Block::Hash>>,
|
||||
block_rules: BlockRules<Block>,
|
||||
execution_extensions: ExecutionExtensions<Block>,
|
||||
config: ClientConfig,
|
||||
config: ClientConfig<Block>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
_phantom: PhantomData<RA>,
|
||||
}
|
||||
@@ -159,10 +159,10 @@ pub fn new_in_mem<E, Block, S, RA>(
|
||||
prometheus_registry: Option<Registry>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
spawn_handle: Box<dyn SpawnNamed>,
|
||||
config: ClientConfig,
|
||||
config: ClientConfig<Block>,
|
||||
) -> sp_blockchain::Result<Client<
|
||||
in_mem::Backend<Block>,
|
||||
LocalCallExecutor<in_mem::Backend<Block>, E>,
|
||||
LocalCallExecutor<Block, in_mem::Backend<Block>, E>,
|
||||
Block,
|
||||
RA
|
||||
>> where
|
||||
@@ -183,14 +183,28 @@ pub fn new_in_mem<E, Block, S, RA>(
|
||||
}
|
||||
|
||||
/// Relevant client configuration items relevant for the client.
|
||||
#[derive(Debug,Clone,Default)]
|
||||
pub struct ClientConfig {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientConfig<Block: BlockT> {
|
||||
/// Enable the offchain worker db.
|
||||
pub offchain_worker_enabled: bool,
|
||||
/// If true, allows access from the runtime to write into offchain worker db.
|
||||
pub offchain_indexing_api: bool,
|
||||
/// Path where WASM files exist to override the on-chain WASM.
|
||||
pub wasm_runtime_overrides: Option<PathBuf>,
|
||||
/// Map of WASM runtime substitute starting at the child of the given block until the runtime
|
||||
/// version doesn't match anymore.
|
||||
pub wasm_runtime_substitutes: HashMap<Block::Hash, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Default for ClientConfig<Block> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
offchain_worker_enabled: false,
|
||||
offchain_indexing_api: false,
|
||||
wasm_runtime_overrides: None,
|
||||
wasm_runtime_substitutes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a client with the explicitly provided backend.
|
||||
@@ -204,8 +218,8 @@ pub fn new_with_backend<B, E, Block, S, RA>(
|
||||
spawn_handle: Box<dyn SpawnNamed>,
|
||||
prometheus_registry: Option<Registry>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
config: ClientConfig,
|
||||
) -> sp_blockchain::Result<Client<B, LocalCallExecutor<B, E>, Block, RA>>
|
||||
config: ClientConfig<Block>,
|
||||
) -> sp_blockchain::Result<Client<B, LocalCallExecutor<Block, B, E>, Block, RA>>
|
||||
where
|
||||
E: CodeExecutor + RuntimeInfo,
|
||||
S: BuildStorage,
|
||||
@@ -308,7 +322,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
execution_extensions: ExecutionExtensions<Block>,
|
||||
prometheus_registry: Option<Registry>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
config: ClientConfig,
|
||||
config: ClientConfig<Block>,
|
||||
) -> sp_blockchain::Result<Self> {
|
||||
if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() {
|
||||
let genesis_storage = build_genesis_storage.build_storage()
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn new_light<B, S, RA, E>(
|
||||
Backend<S, HashFor<B>>,
|
||||
GenesisCallExecutor<
|
||||
Backend<S, HashFor<B>>,
|
||||
LocalCallExecutor<Backend<S, HashFor<B>>, E>
|
||||
LocalCallExecutor<B, Backend<S, HashFor<B>>, E>
|
||||
>,
|
||||
B,
|
||||
RA
|
||||
|
||||
@@ -51,6 +51,7 @@ mod call_executor;
|
||||
mod client;
|
||||
mod block_rules;
|
||||
mod wasm_override;
|
||||
mod wasm_substitutes;
|
||||
|
||||
pub use self::{
|
||||
call_executor::LocalCallExecutor,
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! # WASM substitutes
|
||||
|
||||
use std::{collections::{HashMap, hash_map::DefaultHasher}, hash::Hasher as _, sync::Arc};
|
||||
use sp_core::traits::{FetchRuntimeCode, RuntimeCode};
|
||||
use sp_state_machine::BasicExternalities;
|
||||
use sp_blockchain::{Result, HeaderBackend};
|
||||
use sc_executor::RuntimeInfo;
|
||||
use sp_version::RuntimeVersion;
|
||||
use sc_client_api::backend;
|
||||
use sp_runtime::{traits::{NumberFor, Block as BlockT}, generic::BlockId};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
/// A wasm substitute for the on chain wasm.
|
||||
#[derive(Debug)]
|
||||
struct WasmSubstitute<Block: BlockT> {
|
||||
code: Vec<u8>,
|
||||
hash: Vec<u8>,
|
||||
/// The hash of the block from that on we should use the substitute.
|
||||
block_hash: Block::Hash,
|
||||
/// The block number of `block_hash`. If `None`, the block is still unknown.
|
||||
block_number: RwLock<Option<NumberFor<Block>>>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> WasmSubstitute<Block> {
|
||||
fn new(
|
||||
code: Vec<u8>,
|
||||
block_hash: Block::Hash,
|
||||
backend: &impl backend::Backend<Block>,
|
||||
) -> Result<Self> {
|
||||
let block_number = RwLock::new(backend.blockchain().number(block_hash)?);
|
||||
let hash = make_hash(&code);
|
||||
Ok(Self { code, hash, block_hash, block_number })
|
||||
}
|
||||
|
||||
fn runtime_code(&self, heap_pages: Option<u64>) -> RuntimeCode {
|
||||
RuntimeCode {
|
||||
code_fetcher: self,
|
||||
hash: self.hash.clone(),
|
||||
heap_pages,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` when the substitute matches for the given `block_id`.
|
||||
fn matches(&self, block_id: &BlockId<Block>, backend: &impl backend::Backend<Block>) -> bool {
|
||||
let block_number = *self.block_number.read();
|
||||
let block_number = if let Some(block_number) = block_number {
|
||||
block_number
|
||||
} else {
|
||||
let block_number = match backend.blockchain().number(self.block_hash) {
|
||||
Ok(Some(n)) => n,
|
||||
// still unknown
|
||||
Ok(None) => return false,
|
||||
Err(e) => {
|
||||
log::debug!(
|
||||
target: "wasm_substitutes",
|
||||
"Failed to get block number for block hash {:?}: {:?}",
|
||||
self.block_hash,
|
||||
e,
|
||||
);
|
||||
return false
|
||||
},
|
||||
};
|
||||
*self.block_number.write() = Some(block_number);
|
||||
block_number
|
||||
};
|
||||
|
||||
let requested_block_number = backend.blockchain().block_number_from_id(&block_id).ok().flatten();
|
||||
|
||||
Some(block_number) <= requested_block_number
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a hash out of a byte string using the default rust hasher
|
||||
fn make_hash<K: std::hash::Hash + ?Sized>(val: &K) -> Vec<u8> {
|
||||
let mut state = DefaultHasher::new();
|
||||
val.hash(&mut state);
|
||||
state.finish().to_le_bytes().to_vec()
|
||||
}
|
||||
|
||||
impl<Block: BlockT> FetchRuntimeCode for WasmSubstitute<Block> {
|
||||
fn fetch_runtime_code<'a>(&'a self) -> Option<std::borrow::Cow<'a, [u8]>> {
|
||||
Some(self.code.as_slice().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum WasmSubstituteError {
|
||||
#[error("Failed to get runtime version: {0}")]
|
||||
VersionInvalid(String),
|
||||
}
|
||||
|
||||
impl From<WasmSubstituteError> for sp_blockchain::Error {
|
||||
fn from(err: WasmSubstituteError) -> Self {
|
||||
Self::Application(Box::new(err))
|
||||
}
|
||||
}
|
||||
|
||||
/// Substitutes the on-chain wasm with some hard coded blobs.
|
||||
#[derive(Debug)]
|
||||
pub struct WasmSubstitutes<Block: BlockT, Executor, Backend> {
|
||||
/// spec_version -> WasmSubstitute
|
||||
substitutes: Arc<HashMap<u32, WasmSubstitute<Block>>>,
|
||||
executor: Executor,
|
||||
backend: Arc<Backend>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT, Executor: Clone, Backend> Clone for WasmSubstitutes<Block, Executor, Backend> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
substitutes: self.substitutes.clone(),
|
||||
executor: self.executor.clone(),
|
||||
backend: self.backend.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Executor, Backend, Block> WasmSubstitutes<Block, Executor, Backend>
|
||||
where
|
||||
Executor: RuntimeInfo + Clone + 'static,
|
||||
Backend: backend::Backend<Block>,
|
||||
Block: BlockT,
|
||||
{
|
||||
/// Create a new instance.
|
||||
pub fn new(
|
||||
substitutes: HashMap<Block::Hash, Vec<u8>>,
|
||||
executor: Executor,
|
||||
backend: Arc<Backend>,
|
||||
) -> Result<Self> {
|
||||
let substitutes = substitutes.into_iter().map(|(parent_block_hash, code)| {
|
||||
let substitute = WasmSubstitute::new(code, parent_block_hash, &*backend)?;
|
||||
let version = Self::runtime_version(&executor, &substitute)?;
|
||||
Ok((version.spec_version, substitute))
|
||||
}).collect::<Result<HashMap<_, _>>>()?;
|
||||
|
||||
Ok(Self { executor, substitutes: Arc::new(substitutes), backend })
|
||||
}
|
||||
|
||||
/// Get a substitute.
|
||||
///
|
||||
/// Returns `None` if there isn't any substitute required.
|
||||
pub fn get(
|
||||
&self,
|
||||
spec: u32,
|
||||
pages: Option<u64>,
|
||||
block_id: &BlockId<Block>,
|
||||
) -> Option<RuntimeCode<'_>> {
|
||||
let s = self.substitutes.get(&spec)?;
|
||||
s.matches(block_id, &*self.backend).then(|| s.runtime_code(pages))
|
||||
}
|
||||
|
||||
fn runtime_version(
|
||||
executor: &Executor,
|
||||
code: &WasmSubstitute<Block>,
|
||||
) -> Result<RuntimeVersion> {
|
||||
let mut ext = BasicExternalities::default();
|
||||
executor.runtime_version(&mut ext, &code.runtime_code(None))
|
||||
.map_err(|e| WasmSubstituteError::VersionInvalid(format!("{:?}", e)).into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1822,7 +1822,7 @@ fn cleans_up_closed_notification_sinks_on_block_import() {
|
||||
|
||||
type TestClient = Client<
|
||||
in_mem::Backend<Block>,
|
||||
LocalCallExecutor<in_mem::Backend<Block>, sc_executor::NativeExecutor<LocalExecutor>>,
|
||||
LocalCallExecutor<Block, in_mem::Backend<Block>, sc_executor::NativeExecutor<LocalExecutor>>,
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
substrate_test_runtime_client::runtime::RuntimeApi,
|
||||
>;
|
||||
|
||||
@@ -244,7 +244,7 @@ impl<Block: BlockT, Executor, Backend, G: GenesisInit> TestClientBuilder<Block,
|
||||
|
||||
impl<Block: BlockT, E, Backend, G: GenesisInit> TestClientBuilder<
|
||||
Block,
|
||||
client::LocalCallExecutor<Backend, NativeExecutor<E>>,
|
||||
client::LocalCallExecutor<Block, Backend, NativeExecutor<E>>,
|
||||
Backend,
|
||||
G,
|
||||
> {
|
||||
@@ -255,7 +255,7 @@ impl<Block: BlockT, E, Backend, G: GenesisInit> TestClientBuilder<
|
||||
) -> (
|
||||
client::Client<
|
||||
Backend,
|
||||
client::LocalCallExecutor<Backend, NativeExecutor<E>>,
|
||||
client::LocalCallExecutor<Block, Backend, NativeExecutor<E>>,
|
||||
Block,
|
||||
RuntimeApi
|
||||
>,
|
||||
|
||||
@@ -67,6 +67,7 @@ pub type Backend = substrate_test_client::Backend<substrate_test_runtime::Block>
|
||||
|
||||
/// Test client executor.
|
||||
pub type Executor = client::LocalCallExecutor<
|
||||
substrate_test_runtime::Block,
|
||||
Backend,
|
||||
NativeExecutor<LocalExecutor>,
|
||||
>;
|
||||
@@ -78,6 +79,7 @@ pub type LightBackend = substrate_test_client::LightBackend<substrate_test_runti
|
||||
pub type LightExecutor = sc_light::GenesisCallExecutor<
|
||||
LightBackend,
|
||||
client::LocalCallExecutor<
|
||||
substrate_test_runtime::Block,
|
||||
sc_light::Backend<
|
||||
sc_client_db::light::LightStorage<substrate_test_runtime::Block>,
|
||||
HashFor<substrate_test_runtime::Block>
|
||||
@@ -159,7 +161,11 @@ pub type TestClientBuilder<E, B> = substrate_test_client::TestClientBuilder<
|
||||
/// Test client type with `LocalExecutor` and generic Backend.
|
||||
pub type Client<B> = client::Client<
|
||||
B,
|
||||
client::LocalCallExecutor<B, sc_executor::NativeExecutor<LocalExecutor>>,
|
||||
client::LocalCallExecutor<
|
||||
substrate_test_runtime::Block,
|
||||
B,
|
||||
sc_executor::NativeExecutor<LocalExecutor>
|
||||
>,
|
||||
substrate_test_runtime::Block,
|
||||
substrate_test_runtime::RuntimeApi,
|
||||
>;
|
||||
@@ -245,7 +251,11 @@ pub trait TestClientBuilderExt<B>: Sized {
|
||||
}
|
||||
|
||||
impl<B> TestClientBuilderExt<B> for TestClientBuilder<
|
||||
client::LocalCallExecutor<B, sc_executor::NativeExecutor<LocalExecutor>>,
|
||||
client::LocalCallExecutor<
|
||||
substrate_test_runtime::Block,
|
||||
B,
|
||||
sc_executor::NativeExecutor<LocalExecutor>
|
||||
>,
|
||||
B
|
||||
> where
|
||||
B: sc_client_api::backend::Backend<substrate_test_runtime::Block> + 'static,
|
||||
|
||||
Reference in New Issue
Block a user