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

Commit 874550c

Browse files
pepyakingavofyork
authored andcommitted
Contract signatures checking (#478)
* Convert T in vm::Ext to a associated type * WIP * Fix BTreeMap * Extract prepare module from vm. * Move tests as well. * Fix doc comment. * macro for env defintion * Fix prepare tests. * Clean up * Renamings * Refactor scan_imports * Improve docs * Docs. * Add some tests for sandbox module * Clean up * Use Error::Instantiate instead of ::Deserialize * Add test for imports * Add wrong sig import * Clean up * Rebuild binaries. * Use "another_module" instead of obscure "vne" Since "vne" looks like an actual typo
1 parent fe88e53 commit 874550c

File tree

14 files changed

+1316
-766
lines changed

14 files changed

+1316
-766
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Binary file not shown.

substrate/runtime-sandbox/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ substrate-codec = { path = "../codec", default_features = false }
1616

1717
[dev-dependencies]
1818
wabt = "0.4"
19+
assert_matches = "1.1"
1920

2021
[features]
2122
default = ["std"]

substrate/runtime-sandbox/src/lib.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@
2929
//! When this crate is used in `std` environment all these functions are implemented by directly
3030
//! calling wasm VM.
3131
//!
32-
//! Typical use-case for this library might be used for implementing smart-contract runtimes
33-
//! which uses wasm for contract code.
32+
//! Example of possible use-cases for this library are following:
33+
//!
34+
//! - implementing smart-contract runtimes which uses wasm for contract code
35+
//! - executing wasm substrate runtime inside of a wasm parachain
36+
//! - etc
3437
3538
#![warn(missing_docs)]
3639
#![cfg_attr(not(feature = "std"), no_std)]
@@ -47,6 +50,10 @@ extern crate substrate_primitives as primitives;
4750
#[cfg(test)]
4851
extern crate wabt;
4952

53+
#[cfg(test)]
54+
#[macro_use]
55+
extern crate assert_matches;
56+
5057
use rstd::prelude::*;
5158

5259
pub use primitives::sandbox::{TypedValue, ReturnValue, HostError};
@@ -149,6 +156,11 @@ impl<T> EnvironmentDefinitionBuilder<T> {
149156
}
150157

151158
/// Register a host function in this environment defintion.
159+
///
160+
/// NOTE that there is no constraints on type of this function. An instance
161+
/// can import function passed here with any signature it wants. It can even import
162+
/// the same function (i.e. with same `module` and `field`) several times. It's up to
163+
/// the user code to check or constrain the types of signatures.
152164
pub fn add_host_func<N1, N2>(&mut self, module: N1, field: N2, f: HostFuncType<T>)
153165
where
154166
N1: Into<Vec<u8>>,

substrate/runtime-sandbox/with_std.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ impl<T> Instance<T> {
309309
#[cfg(test)]
310310
mod tests {
311311
use wabt;
312-
use ::{TypedValue, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance};
312+
use ::{Error, TypedValue, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance};
313313

314314
fn execute_sandboxed(code: &[u8], args: &[TypedValue]) -> Result<ReturnValue, HostError> {
315315
struct State {
@@ -335,12 +335,20 @@ mod tests {
335335
e.counter += inc_by as u32;
336336
Ok(ReturnValue::Value(TypedValue::I32(e.counter as i32)))
337337
}
338+
/// Function that takes one argument of any type and returns that value.
339+
fn env_polymorphic_id(_e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
340+
if args.len() != 1 {
341+
return Err(HostError);
342+
}
343+
Ok(ReturnValue::Value(args[0]))
344+
}
338345

339346
let mut state = State { counter: 0 };
340347

341348
let mut env_builder = EnvironmentDefinitionBuilder::new();
342349
env_builder.add_host_func("env", "assert", env_assert);
343350
env_builder.add_host_func("env", "inc_counter", env_inc_counter);
351+
env_builder.add_host_func("env", "polymorphic_id", env_polymorphic_id);
344352

345353
let mut instance = Instance::new(code, &env_builder, &mut state)?;
346354
let result = instance.invoke(b"call", args, &mut state);
@@ -404,4 +412,71 @@ mod tests {
404412
).unwrap();
405413
assert_eq!(return_val, ReturnValue::Value(TypedValue::I32(0x1337)));
406414
}
415+
416+
#[test]
417+
fn signatures_dont_matter() {
418+
let code = wabt::wat2wasm(r#"
419+
(module
420+
(import "env" "polymorphic_id" (func $id_i32 (param i32) (result i32)))
421+
(import "env" "polymorphic_id" (func $id_i64 (param i64) (result i64)))
422+
(import "env" "assert" (func $assert (param i32)))
423+
424+
(func (export "call")
425+
;; assert that we can actually call the "same" function with different
426+
;; signatures.
427+
(call $assert
428+
(i32.eq
429+
(call $id_i32
430+
(i32.const 0x012345678)
431+
)
432+
(i32.const 0x012345678)
433+
)
434+
)
435+
(call $assert
436+
(i64.eq
437+
(call $id_i64
438+
(i64.const 0x0123456789abcdef)
439+
)
440+
(i64.const 0x0123456789abcdef)
441+
)
442+
)
443+
)
444+
)
445+
"#).unwrap();
446+
447+
let return_val = execute_sandboxed(&code, &[]).unwrap();
448+
assert_eq!(return_val, ReturnValue::Unit);
449+
}
450+
451+
#[test]
452+
fn cant_return_unmatching_type() {
453+
fn env_returns_i32(_e: &mut (), _args: &[TypedValue]) -> Result<ReturnValue, HostError> {
454+
Ok(ReturnValue::Value(TypedValue::I32(42)))
455+
}
456+
457+
let mut env_builder = EnvironmentDefinitionBuilder::new();
458+
env_builder.add_host_func("env", "returns_i32", env_returns_i32);
459+
460+
let code = wabt::wat2wasm(r#"
461+
(module
462+
;; It's actually returns i32, but imported as if it returned i64
463+
(import "env" "returns_i32" (func $returns_i32 (result i64)))
464+
465+
(func (export "call")
466+
(drop
467+
(call $returns_i32)
468+
)
469+
)
470+
)
471+
"#).unwrap();
472+
473+
// It succeeds since we are able to import functions with types we want.
474+
let mut instance = Instance::new(&code, &env_builder, &mut ()).unwrap();
475+
476+
// But this fails since we imported a function that returns i32 as if it returned i64.
477+
assert_matches!(
478+
instance.invoke(b"call", &[], &mut ()),
479+
Err(Error::Execution)
480+
);
481+
}
407482
}

substrate/runtime/contract/src/exec.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ struct CallContext<'a, 'b: 'a, T: Trait + 'b> {
226226
_caller: T::AccountId,
227227
}
228228

229-
impl<'a, 'b: 'a, T: Trait + 'b> vm::Ext<T> for CallContext<'a, 'b, T> {
229+
impl<'a, 'b: 'a, T: Trait + 'b> vm::Ext for CallContext<'a, 'b, T> {
230+
type T = T;
231+
230232
fn get_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
231233
self.ctx.overlay.get_storage(&self.ctx.self_account, key)
232234
}

0 commit comments

Comments
 (0)