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

Commit

Permalink
WASM Local-blob override (#7317)
Browse files Browse the repository at this point in the history
* Provide WASM overwrite functionality in LocalCallExecutor

- add a new module `wasm_overwrite.rs` in client
  - scrapes given folder for runtimes
- add two new CLI Options `wasm-overwrite` and `wasm_overwrite_path`

* formatting

* Make comment clearer

remove sc-runtime-test from dev-dependencies

* comments

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* Fix spaces, remove call into backend for 'heap_pages' in 'try_replace'

* Error if path is not a directory, Comments,

Doc Comment for WasmOverwrite

* make WasmOverwrite Option<>

* Change to one CLI argument for overwrites

- move getting runtime version into LocalCallExecutor

* change unwrap() to expect()

* comment

* Remove `check_overwrites`

* Encapsulate checking for overwrites in LocalCallExecutor

* move duplicate code into function

* Update client/cli/src/params/import_params.rs

Co-authored-by: Bastian Köcher <[email protected]>

* comma

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* cache hash in WasmBlob

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* Update client/service/src/client/client.rs

Co-authored-by: Bastian Köcher <[email protected]>

* move getting overwrite into its own function

* fix error when directory is not a directory

* Error on duplicate WASM runtimes

* better comment, grammar

* docs

* Revert StateBackend back to _

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* Update client/service/src/client/call_executor.rs

Co-authored-by: Bastian Köcher <[email protected]>

* Add two tests, fix doc comments

Add a test for the runtime_version method of WasmOverwrite
Add a test for check_overwrite method of LocalCallExecutor

* remove redundant `Return` from expect msg

* Update client/cli/src/params/import_params.rs

Co-authored-by: David <[email protected]>

* Update client/service/src/client/call_executor.rs

Co-authored-by: David <[email protected]>

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: David <[email protected]>

* Update client/service/src/config.rs

Co-authored-by: David <[email protected]>

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: David <[email protected]>

* Add Module Documentation, match on '.wasm' extension

* Add test for scraping WASM blob

* fix expect

* remove creating another block in LocalCallExecutor test

* remove unused import

* add tests for duplicates and scraping wasm

* make tests a bit nicer

* add test for ignoring non-.wasm files

* check error message in test

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* remove println

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* make tests prettier

* Update client/service/src/client/wasm_overwrite.rs

Co-authored-by: Bastian Köcher <[email protected]>

* comment for seemingly random client

* locally-built -> custom

* remove unused import

* fix comment

* rename all references to overwrite with override

* fix cli flag in module documentation

Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: David <[email protected]>
  • Loading branch information
3 people committed Oct 26, 2020
1 parent 891900a commit d766e22
Show file tree
Hide file tree
Showing 15 changed files with 420 additions and 15 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions client/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,15 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
.unwrap_or_default())
}

/// Get the path where WASM overrides live.
///
/// By default this is `None`.
fn wasm_runtime_overrides(&self) -> Option<PathBuf> {
self.import_params()
.map(|x| x.wasm_runtime_overrides())
.unwrap_or_default()
}

/// Get the execution strategies.
///
/// By default this is retrieved from `ImportParams` if it is available. Otherwise its
Expand Down Expand Up @@ -492,6 +501,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
state_cache_child_ratio: self.state_cache_child_ratio()?,
pruning: self.pruning(unsafe_pruning, &role)?,
wasm_method: self.wasm_method()?,
wasm_runtime_overrides: self.wasm_runtime_overrides(),
execution_strategies: self.execution_strategies(is_dev, is_validator)?,
rpc_http: self.rpc_http(DCV::rpc_http_listen_port())?,
rpc_ws: self.rpc_ws(DCV::rpc_ws_listen_port())?,
Expand Down
13 changes: 13 additions & 0 deletions client/cli/src/params/import_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::params::DatabaseParams;
use crate::params::PruningParams;
use sc_client_api::execution_extensions::ExecutionStrategies;
use structopt::StructOpt;
use std::path::PathBuf;

/// Parameters for block import.
#[derive(Debug, StructOpt)]
Expand Down Expand Up @@ -55,6 +56,12 @@ pub struct ImportParams {
)]
pub wasm_method: WasmExecutionMethod,

/// Specify the path where local WASM runtimes are stored.
///
/// These runtimes will override on-chain runtimes when the version matches.
#[structopt(long, value_name = "PATH", parse(from_os_str))]
pub wasm_runtime_overrides: Option<PathBuf>,

#[allow(missing_docs)]
#[structopt(flatten)]
pub execution_strategies: ExecutionStrategiesParams,
Expand Down Expand Up @@ -103,6 +110,12 @@ impl ImportParams {
self.wasm_method.into()
}

/// Enable overriding on-chain WASM with locally-stored WASM
/// by specifying the path where local WASM is stored.
pub fn wasm_runtime_overrides(&self) -> Option<PathBuf> {
self.wasm_runtime_overrides.clone()
}

/// Get execution strategies for the parameters
pub fn execution_strategies(&self, is_dev: bool, is_validator: bool) -> ExecutionStrategies {
let exec = &self.execution_strategies;
Expand Down
1 change: 1 addition & 0 deletions client/service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ directories = "2.0.2"

[dev-dependencies]
substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" }
substrate-test-runtime = { versino = "2.0.0", path = "../../test-utils/runtime/" }
sp-consensus-babe = { version = "0.8.0", path = "../../primitives/consensus/babe" }
grandpa = { version = "0.8.0", package = "sc-finality-grandpa", path = "../finality-grandpa" }
grandpa-primitives = { version = "2.0.0", package = "sp-finality-grandpa", path = "../../primitives/finality-grandpa" }
Expand Down
5 changes: 3 additions & 2 deletions client/service/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,9 @@ pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
Box::new(task_manager.spawn_handle()),
config.prometheus_config.as_ref().map(|config| config.registry.clone()),
ClientConfig {
offchain_worker_enabled : config.offchain_worker.enabled ,
offchain_worker_enabled : config.offchain_worker.enabled,
offchain_indexing_api: config.offchain_worker.indexing_enabled,
wasm_runtime_overrides: config.wasm_runtime_overrides.clone(),
},
)?
};
Expand Down Expand Up @@ -396,7 +397,7 @@ pub fn new_client<E, Block, RA>(
const CANONICALIZATION_DELAY: u64 = 4096;

let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?);
let executor = crate::client::LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone());
let executor = crate::client::LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone())?;
Ok((
crate::client::Client::new(
backend.clone(),
Expand Down
120 changes: 111 additions & 9 deletions client/service/src/client/call_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,71 @@ use sp_state_machine::{
use sc_executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
use sp_externalities::Extensions;
use sp_core::{
NativeOrEncoded, NeverNativeValue, traits::{CodeExecutor, SpawnNamed},
NativeOrEncoded, NeverNativeValue, traits::{CodeExecutor, SpawnNamed, RuntimeCode},
offchain::storage::OffchainOverlayedChanges,
};
use sp_api::{ProofRecorder, InitializeBlock, StorageTransactionCache};
use sc_client_api::{backend, call_executor::CallExecutor};
use super::client::ClientConfig;
use super::{client::ClientConfig, wasm_override::WasmOverride};

/// Call executor that executes methods locally, querying all required
/// data from local backend.
pub struct LocalCallExecutor<B, E> {
backend: Arc<B>,
executor: E,
wasm_override: Option<WasmOverride<E>>,
spawn_handle: Box<dyn SpawnNamed>,
client_config: ClientConfig,
}

impl<B, E> LocalCallExecutor<B, E> {
impl<B, E> LocalCallExecutor<B, E>
where
E: CodeExecutor + RuntimeInfo + Clone + 'static
{
/// Creates new instance of local call executor.
pub fn new(
backend: Arc<B>,
executor: E,
spawn_handle: Box<dyn SpawnNamed>,
client_config: ClientConfig,
) -> Self {
LocalCallExecutor {
) -> sp_blockchain::Result<Self> {
let wasm_override = client_config.wasm_runtime_overrides
.as_ref()
.map(|p| WasmOverride::new(p.clone(), executor.clone()))
.transpose()?;

Ok(LocalCallExecutor {
backend,
executor,
wasm_override,
spawn_handle,
client_config,
}
})
}

/// 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>(
&'a self,
onchain_code: RuntimeCode<'a>,
id: &BlockId<Block>,
) -> sp_blockchain::Result<RuntimeCode<'a>>
where
Block: BlockT,
B: backend::Backend<Block>,
{
let code = 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()?
.flatten()
.unwrap_or(onchain_code);

Ok(code)
}
}

Expand All @@ -66,6 +101,7 @@ impl<B, E> Clone for LocalCallExecutor<B, E> where E: Clone {
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(),
}
Expand Down Expand Up @@ -101,6 +137,8 @@ where
)?;
let state = self.backend.state_at(*id)?;
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
let runtime_code = self.check_override(state_runtime_code.runtime_code()?, id)?;

let return_data = StateMachine::new(
&state,
changes_trie,
Expand All @@ -110,7 +148,7 @@ where
method,
call_data,
extensions.unwrap_or_default(),
&state_runtime_code.runtime_code()?,
&runtime_code,
self.spawn_handle.clone(),
).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>(
strategy.get_manager(),
Expand Down Expand Up @@ -173,7 +211,7 @@ where
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&trie_state);
// It is important to extract the runtime code here before we create the proof
// recorder.
let runtime_code = state_runtime_code.runtime_code()?;
let runtime_code = self.check_override(state_runtime_code.runtime_code()?, at)?;

let backend = sp_state_machine::ProvingBackend::new_with_recorder(
trie_state,
Expand All @@ -198,7 +236,8 @@ where
},
None => {
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
let runtime_code = state_runtime_code.runtime_code()?;
let runtime_code = self.check_override(state_runtime_code.runtime_code()?, at)?;

let mut state_machine = StateMachine::new(
&state,
changes_trie_state,
Expand Down Expand Up @@ -279,3 +318,66 @@ impl<B, E, Block> sp_version::GetRuntimeVersion<Block> for LocalCallExecutor<B,
CallExecutor::runtime_version(self, at).map_err(|e| format!("{:?}", e))
}
}

#[cfg(test)]
mod tests {
use super::*;
use substrate_test_runtime_client::{LocalExecutor, GenesisInit, runtime};
use sc_executor::{NativeExecutor, WasmExecutionMethod};
use sp_core::{traits::{WrappedRuntimeCode, FetchRuntimeCode}, testing::TaskExecutor};
use sc_client_api::in_mem;

#[test]
fn should_get_override_if_exists() {
let executor =
NativeExecutor::<LocalExecutor>::new(WasmExecutionMethod::Interpreted, Some(128), 1);

let overrides = crate::client::wasm_override::dummy_overrides(&executor);
let onchain_code = WrappedRuntimeCode(substrate_test_runtime::wasm_binary_unwrap().into());
let onchain_code = RuntimeCode {
code_fetcher: &onchain_code,
heap_pages: Some(128),
hash: vec![0, 0, 0, 0],
};

let backend = Arc::new(in_mem::Backend::<runtime::Block>::new());

// 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,
};

// client is used for the convenience of creating and inserting the genesis block.
let _client = substrate_test_runtime_client::client::new_with_backend::<
_,
_,
runtime::Block,
_,
runtime::RuntimeApi,
>(
backend.clone(),
executor.clone(),
&substrate_test_runtime_client::GenesisParameters::default().genesis_storage(),
None,
Box::new(TaskExecutor::new()),
None,
Default::default(),
).expect("Creates a client");

let call_executor = LocalCallExecutor {
backend: backend.clone(),
executor,
wasm_override: Some(overrides),
spawn_handle: Box::new(TaskExecutor::new()),
client_config,
};

let check = call_executor.check_override(onchain_code, &BlockId::Number(Default::default()))
.expect("RuntimeCode override");

assert_eq!(Some(vec![2, 2, 2, 2, 2, 2, 2, 2]), check.fetch_runtime_code().map(Into::into));
}
}
5 changes: 4 additions & 1 deletion client/service/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::{
marker::PhantomData,
collections::{HashSet, BTreeMap, HashMap},
sync::Arc, panic::UnwindSafe, result,
path::PathBuf
};
use log::{info, trace, warn};
use parking_lot::{Mutex, RwLock};
Expand Down Expand Up @@ -181,6 +182,8 @@ pub struct ClientConfig {
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>,
}

/// Create a client with the explicitly provided backend.
Expand All @@ -201,7 +204,7 @@ pub fn new_with_backend<B, E, Block, S, RA>(
Block: BlockT,
B: backend::LocalBackend<Block> + 'static,
{
let call_executor = LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone());
let call_executor = LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone())?;
let extensions = ExecutionExtensions::new(Default::default(), keystore);
Client::new(
backend,
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 @@ -60,7 +60,7 @@ pub fn new_light<B, S, RA, E>(
code_executor,
spawn_handle.clone(),
ClientConfig::default()
);
)?;
let executor = GenesisCallExecutor::new(backend.clone(), local_executor);
Client::new(
backend,
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 @@ -49,6 +49,7 @@ pub mod light;
mod call_executor;
mod client;
mod block_rules;
mod wasm_override;

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

0 comments on commit d766e22

Please sign in to comment.