Skip to content

Commit 594db90

Browse files
bkchrMTDK1
authored andcommitted
Prerequisites for validate_block in Cumulus (paritytech#1926)
* Adds benchmark for direct/indirect wasm function calls * Store the benchmark function pointer in a `Cell` * Add some documentation * Make function implementations exchangeable * Add parachain stub * Add macro for registering the `validate_block` function * Make all functions replace-able by unimplemented * Some more refactoring * Adds tests for executing empty parachain block * Work on a new test with empty witness data * Don't exchange `ext_print_*` stuff * Some cleanup and one more function for `validate_block` * More tests and more functions * Fixes after merging master * Use `parity-codec` `derive` feature * CHange implementation of `wasm-nice-panic-message` * Move `parachain` stuff to cumulus * Updated wasm files * Integrate feedback * Switch to `ExchangeableFunction` struct * More fixes * Switch to Cell and panic on multiple replaces * Increase `impl_version` * Fix shifting * Make the API more verbose of `ExchangeableFunction` * Increase `impl_version`
1 parent 488be21 commit 594db90

File tree

17 files changed

+577
-169
lines changed

17 files changed

+577
-169
lines changed

Cargo.lock

Lines changed: 18 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/executor/src/wasm_executor.rs

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use secp256k1;
2323
use wasmi::{
2424
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef,
2525
};
26-
use wasmi::RuntimeValue::{I32, I64};
26+
use wasmi::RuntimeValue::{I32, I64, self};
2727
use wasmi::memory_units::{Pages};
2828
use state_machine::Externalities;
2929
use crate::error::{Error, ErrorKind, Result};
@@ -393,10 +393,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
393393
.map_err(|_| UserError("Invalid attempt to get parent_hash in ext_storage_changes_root"))?;
394394
parent_hash.as_mut().copy_from_slice(&raw_parent_hash[..]);
395395
let r = this.ext.storage_changes_root(parent_hash, parent_number);
396-
if let Some(ref r) = r {
396+
if let Some(r) = r {
397397
this.memory.set(result, &r[..]).map_err(|_| UserError("Invalid attempt to set memory in ext_storage_changes_root"))?;
398+
Ok(1)
399+
} else {
400+
Ok(0)
398401
}
399-
Ok(if r.is_some() { 1u32 } else { 0u32 })
400402
},
401403
ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8) => {
402404
let values = (0..lens_len)
@@ -637,17 +639,19 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
637639
///
638640
/// Executes the provided code in a sandboxed wasm runtime.
639641
#[derive(Debug, Clone)]
640-
pub struct WasmExecutor {
641-
}
642+
pub struct WasmExecutor;
642643

643644
impl WasmExecutor {
644645

645646
/// Create a new instance.
646647
pub fn new() -> Self {
647-
WasmExecutor{}
648+
WasmExecutor
648649
}
649650

650651
/// Call a given method in the given code.
652+
///
653+
/// Signature of this method needs to be `(I32, I32) -> I64`.
654+
///
651655
/// This should be used for tests only.
652656
pub fn call<E: Externalities<Blake2Hasher>>(
653657
&self,
@@ -656,12 +660,34 @@ impl WasmExecutor {
656660
code: &[u8],
657661
method: &str,
658662
data: &[u8],
659-
) -> Result<Vec<u8>> {
663+
) -> Result<Vec<u8>> {
660664
let module = ::wasmi::Module::from_buffer(code)?;
661665
let module = self.prepare_module(ext, heap_pages, &module)?;
662666
self.call_in_wasm_module(ext, &module, method, data)
663667
}
664668

669+
/// Call a given method with a custom signature in the given code.
670+
///
671+
/// This should be used for tests only.
672+
pub fn call_with_custom_signature<
673+
E: Externalities<Blake2Hasher>,
674+
F: FnOnce(&mut FnMut(&[u8]) -> Result<u32>) -> Result<Vec<RuntimeValue>>,
675+
FR: FnOnce(Option<RuntimeValue>, &MemoryRef) -> Result<Option<R>>,
676+
R,
677+
>(
678+
&self,
679+
ext: &mut E,
680+
heap_pages: usize,
681+
code: &[u8],
682+
method: &str,
683+
create_parameters: F,
684+
filter_result: FR,
685+
) -> Result<R> {
686+
let module = wasmi::Module::from_buffer(code)?;
687+
let module = self.prepare_module(ext, heap_pages, &module)?;
688+
self.call_in_wasm_module_with_custom_signature(ext, &module, method, create_parameters, filter_result)
689+
}
690+
665691
fn get_mem_instance(module: &ModuleRef) -> Result<MemoryRef> {
666692
Ok(module
667693
.export_by_name("memory")
@@ -679,6 +705,40 @@ impl WasmExecutor {
679705
method: &str,
680706
data: &[u8],
681707
) -> Result<Vec<u8>> {
708+
self.call_in_wasm_module_with_custom_signature(
709+
ext,
710+
module_instance,
711+
method,
712+
|alloc| {
713+
let offset = alloc(data)?;
714+
Ok(vec![I32(offset as i32), I32(data.len() as i32)])
715+
},
716+
|res, memory| {
717+
if let Some(I64(r)) = res {
718+
let offset = r as u32;
719+
let length = (r as u64 >> 32) as usize;
720+
memory.get(offset, length).map_err(|_| ErrorKind::Runtime.into()).map(Some)
721+
} else {
722+
Ok(None)
723+
}
724+
}
725+
)
726+
}
727+
728+
/// Call a given method in the given wasm-module runtime.
729+
fn call_in_wasm_module_with_custom_signature<
730+
E: Externalities<Blake2Hasher>,
731+
F: FnOnce(&mut FnMut(&[u8]) -> Result<u32>) -> Result<Vec<RuntimeValue>>,
732+
FR: FnOnce(Option<RuntimeValue>, &MemoryRef) -> Result<Option<R>>,
733+
R,
734+
>(
735+
&self,
736+
ext: &mut E,
737+
module_instance: &ModuleRef,
738+
method: &str,
739+
create_parameters: F,
740+
filter_result: FR,
741+
) -> Result<R> {
682742
// extract a reference to a linear memory, optional reference to a table
683743
// and then initialize FunctionExecutor.
684744
let memory = Self::get_mem_instance(module_instance)?;
@@ -689,26 +749,22 @@ impl WasmExecutor {
689749
let low = memory.lowest_used();
690750
let used_mem = memory.used_size();
691751
let mut fec = FunctionExecutor::new(memory.clone(), table, ext)?;
692-
let size = data.len() as u32;
693-
let offset = fec.heap.allocate(size).map_err(|_| ErrorKind::Runtime)?;
694-
memory.set(offset, &data)?;
752+
let parameters = create_parameters(&mut |data: &[u8]| {
753+
let offset = fec.heap.allocate(data.len() as u32).map_err(|_| ErrorKind::Runtime)?;
754+
memory.set(offset, &data)?;
755+
Ok(offset)
756+
})?;
695757

696758
let result = module_instance.invoke_export(
697759
method,
698-
&[
699-
I32(offset as i32),
700-
I32(size as i32)
701-
],
760+
&parameters,
702761
&mut fec
703762
);
704763
let result = match result {
705-
Ok(Some(I64(r))) => {
706-
let offset = r as u32;
707-
let length = (r >> 32) as u32 as usize;
708-
memory.get(offset, length)
709-
.map_err(|_| ErrorKind::Runtime.into())
764+
Ok(val) => match filter_result(val, &memory)? {
765+
Some(val) => Ok(val),
766+
None => Err(ErrorKind::InvalidReturn.into()),
710767
},
711-
Ok(_) => Err(ErrorKind::InvalidReturn.into()),
712768
Err(e) => {
713769
trace!(target: "wasm-executor", "Failed to execute code with {} pages", memory.current_size().0);
714770
Err(e.into())
@@ -738,7 +794,7 @@ impl WasmExecutor {
738794
module,
739795
&ImportsBuilder::new()
740796
.with_resolver("env", FunctionExecutor::<E>::resolver())
741-
)?;
797+
)?;
742798

743799
// extract a reference to a linear memory, optional reference to a table
744800
// and then initialize FunctionExecutor.
@@ -759,7 +815,7 @@ impl WasmExecutor {
759815
#[cfg(test)]
760816
mod tests {
761817
use super::*;
762-
818+
763819
use parity_codec::Encode;
764820

765821
use state_machine::TestExternalities;

core/sr-api-macros/benches/bench.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
1616

17-
use criterion::Criterion;
17+
use criterion::{Criterion, criterion_group, criterion_main};
1818
use test_client::runtime::TestAPI;
1919
use runtime_primitives::{generic::BlockId, traits::ProvideRuntimeApi};
20+
use state_machine::ExecutionStrategy;
2021

2122
fn sr_api_benchmark(c: &mut Criterion) {
2223
c.bench_function("add one with same runtime api", |b| {
@@ -50,6 +51,18 @@ fn sr_api_benchmark(c: &mut Criterion) {
5051

5152
b.iter_with_large_drop(|| client.runtime_api().benchmark_vector_add_one(&block_id, &data))
5253
});
54+
55+
c.bench_function("calling function by function pointer in wasm", |b| {
56+
let client = test_client::new_with_execution_strategy(ExecutionStrategy::AlwaysWasm);
57+
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
58+
b.iter(|| client.runtime_api().benchmark_indirect_call(&block_id).unwrap())
59+
});
60+
61+
c.bench_function("calling function in wasm", |b| {
62+
let client = test_client::new_with_execution_strategy(ExecutionStrategy::AlwaysWasm);
63+
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
64+
b.iter(|| client.runtime_api().benchmark_direct_call(&block_id).unwrap())
65+
});
5366
}
5467

5568
criterion_group!(benches, sr_api_benchmark);

core/sr-io/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ std = [
3535
]
3636
nightly = []
3737
strict = []
38+
wasm-nice-panic-message = []

0 commit comments

Comments
 (0)