Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Introduce code_substitute #8898

Merged
1 commit merged into from
May 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bin/node/testing/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
>;
Expand Down Expand Up @@ -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,
> {
Expand Down
13 changes: 12 additions & 1 deletion client/chain-spec/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a future pr I would like to replace String with something that directly checks that we got here some hex value.

}

/// A type denoting empty extensions.
Expand Down Expand Up @@ -249,6 +255,7 @@ impl<G, E> ChainSpec<G, E> {
consensus_engine: (),
genesis: Default::default(),
light_sync_state: None,
code_substitutes: HashMap::new(),
};

ChainSpec {
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions client/chain-spec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
25 changes: 20 additions & 5 deletions client/service/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>,
>;
Expand All @@ -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>
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)?;

Expand Down Expand Up @@ -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,
Expand All @@ -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,
},
)?;

Expand Down Expand Up @@ -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,
>,
Expand Down
53 changes: 32 additions & 21 deletions client/service/src/client/call_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand All @@ -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",
Expand All @@ -104,19 +113,20 @@ 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(),
executor: self.executor.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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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::<
Expand All @@ -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()))
Expand Down
30 changes: 22 additions & 8 deletions client/service/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}
Expand Down Expand Up @@ -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
Expand All @@ -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.
Comment on lines +194 to +195
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be good to point out that the wasm_runtime_overrides trumps the wasm_runtime_substitutes?

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.
Expand All @@ -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,
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion client/service/src/client/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions client/service/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mod call_executor;
mod client;
mod block_rules;
mod wasm_override;
mod wasm_substitutes;

pub use self::{
call_executor::LocalCallExecutor,
Expand Down
Loading