mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-08 18:17:22 +00:00
srml-contracts: Contract calls/instantiations to return exit statuses (#3320)
* srml-contracts: Rename ext_scratch_copy to ext_scratch_read. This is to disambiguate from the next ext_scratch_write function. * Remove unnecessary OutputBuf and EmptyOutputBuf. * Replace VmExecError with a result type of custom structs. * Do not drop the scratch buffer on traps and regular returns. This just reduces the number of allocations required during nested contract calls and instantiations. * Semantics for returning a status code and data from contract calls. * Remove CallReceipt and InstantiateReceipt. With forthcoming changes to return data from instantiate calls, the two types of receipts become very similar to each other and to ExecReturnValue. Instead, replace them with ExecReturnValue and a regular 2-tuple in the case of instantiation. * Modify contract function signatures to allow returning status codes. * Introduce ext_sandbox_write runtime function. * Test all the things. * Bump node runtime spec version. * Style fixes.
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
|
||||
use crate::{CodeHash, Schedule, Trait};
|
||||
use crate::wasm::env_def::FunctionImplProvider;
|
||||
use crate::exec::{Ext, EmptyOutputBuf, VmExecResult};
|
||||
use crate::exec::{Ext, ExecResult};
|
||||
use crate::gas::GasMeter;
|
||||
|
||||
use rstd::prelude::*;
|
||||
@@ -110,10 +110,9 @@ impl<'a, T: Trait> crate::exec::Vm<T> for WasmVm<'a> {
|
||||
&self,
|
||||
exec: &WasmExecutable,
|
||||
mut ext: E,
|
||||
input_data: &[u8],
|
||||
empty_output_buf: EmptyOutputBuf,
|
||||
input_data: Vec<u8>,
|
||||
gas_meter: &mut GasMeter<E::T>,
|
||||
) -> VmExecResult {
|
||||
) -> ExecResult {
|
||||
let memory =
|
||||
sandbox::Memory::new(exec.prefab_module.initial, Some(exec.prefab_module.maximum))
|
||||
.unwrap_or_else(|_| {
|
||||
@@ -134,38 +133,17 @@ impl<'a, T: Trait> crate::exec::Vm<T> for WasmVm<'a> {
|
||||
|
||||
let mut runtime = Runtime::new(
|
||||
&mut ext,
|
||||
input_data.to_vec(),
|
||||
empty_output_buf,
|
||||
input_data,
|
||||
&self.schedule,
|
||||
memory,
|
||||
gas_meter,
|
||||
);
|
||||
|
||||
// Instantiate the instance from the instrumented module code.
|
||||
match sandbox::Instance::new(&exec.prefab_module.code, &imports, &mut runtime) {
|
||||
// No errors or traps were generated on instantiation! That
|
||||
// means we can now invoke the contract entrypoint.
|
||||
Ok(mut instance) => {
|
||||
let err = instance
|
||||
.invoke(exec.entrypoint_name, &[], &mut runtime)
|
||||
.err();
|
||||
to_execution_result(runtime, err)
|
||||
}
|
||||
// `start` function trapped. Treat it in the same manner as an execution error.
|
||||
Err(err @ sandbox::Error::Execution) => to_execution_result(runtime, Some(err)),
|
||||
Err(_err @ sandbox::Error::Module) => {
|
||||
// `Error::Module` is returned only if instantiation or linking failed (i.e.
|
||||
// wasm binary tried to import a function that is not provided by the host).
|
||||
// This shouldn't happen because validation process ought to reject such binaries.
|
||||
//
|
||||
// Because panics are really undesirable in the runtime code, we treat this as
|
||||
// a trap for now. Eventually, we might want to revisit this.
|
||||
return VmExecResult::Trap("validation error");
|
||||
}
|
||||
// Other instantiation errors.
|
||||
// Return without executing anything.
|
||||
Err(_) => return VmExecResult::Trap("during start function"),
|
||||
}
|
||||
// Instantiate the instance from the instrumented module code and invoke the contract
|
||||
// entrypoint.
|
||||
let result = sandbox::Instance::new(&exec.prefab_module.code, &imports, &mut runtime)
|
||||
.and_then(|mut instance| instance.invoke(exec.entrypoint_name, &[], &mut runtime));
|
||||
to_execution_result(runtime, result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,16 +152,18 @@ mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
use primitives::H256;
|
||||
use crate::exec::{CallReceipt, Ext, InstantiateReceipt, EmptyOutputBuf, StorageKey};
|
||||
use crate::exec::{Ext, StorageKey, ExecError, ExecReturnValue, STATUS_SUCCESS};
|
||||
use crate::gas::{Gas, GasMeter};
|
||||
use crate::tests::{Test, Call};
|
||||
use crate::wasm::prepare::prepare_contract;
|
||||
use crate::CodeHash;
|
||||
use wabt;
|
||||
use hex_literal::hex;
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct DispatchEntry(Call);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct RestoreEntry {
|
||||
dest: u64,
|
||||
@@ -191,6 +171,7 @@ mod tests {
|
||||
rent_allowance: u64,
|
||||
delta: Vec<StorageKey>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct CreateEntry {
|
||||
code_hash: H256,
|
||||
@@ -198,6 +179,7 @@ mod tests {
|
||||
data: Vec<u8>,
|
||||
gas_left: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct TransferEntry {
|
||||
to: u64,
|
||||
@@ -205,6 +187,7 @@ mod tests {
|
||||
data: Vec<u8>,
|
||||
gas_left: u64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MockExt {
|
||||
storage: HashMap<StorageKey, Vec<u8>>,
|
||||
@@ -217,6 +200,7 @@ mod tests {
|
||||
events: Vec<(Vec<H256>, Vec<u8>)>,
|
||||
next_account_id: u64,
|
||||
}
|
||||
|
||||
impl Ext for MockExt {
|
||||
type T = Test;
|
||||
|
||||
@@ -234,8 +218,8 @@ mod tests {
|
||||
code_hash: &CodeHash<Test>,
|
||||
endowment: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
data: &[u8],
|
||||
) -> Result<InstantiateReceipt<u64>, &'static str> {
|
||||
data: Vec<u8>,
|
||||
) -> Result<(u64, ExecReturnValue), ExecError> {
|
||||
self.creates.push(CreateEntry {
|
||||
code_hash: code_hash.clone(),
|
||||
endowment,
|
||||
@@ -245,16 +229,15 @@ mod tests {
|
||||
let address = self.next_account_id;
|
||||
self.next_account_id += 1;
|
||||
|
||||
Ok(InstantiateReceipt { address })
|
||||
Ok((address, ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() }))
|
||||
}
|
||||
fn call(
|
||||
&mut self,
|
||||
to: &u64,
|
||||
value: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
data: &[u8],
|
||||
_output_data: EmptyOutputBuf,
|
||||
) -> Result<CallReceipt, &'static str> {
|
||||
data: Vec<u8>,
|
||||
) -> ExecResult {
|
||||
self.transfers.push(TransferEntry {
|
||||
to: *to,
|
||||
value,
|
||||
@@ -263,9 +246,7 @@ mod tests {
|
||||
});
|
||||
// Assume for now that it was just a plain transfer.
|
||||
// TODO: Add tests for different call outcomes.
|
||||
Ok(CallReceipt {
|
||||
output_data: Vec::new(),
|
||||
})
|
||||
Ok(ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() })
|
||||
}
|
||||
fn note_dispatch_call(&mut self, call: Call) {
|
||||
self.dispatches.push(DispatchEntry(call));
|
||||
@@ -321,6 +302,7 @@ mod tests {
|
||||
|
||||
fn max_value_size(&self) -> u32 { 16_384 }
|
||||
}
|
||||
|
||||
impl Ext for &mut MockExt {
|
||||
type T = <MockExt as Ext>::T;
|
||||
|
||||
@@ -337,8 +319,8 @@ mod tests {
|
||||
code: &CodeHash<Test>,
|
||||
value: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
input_data: &[u8]
|
||||
) -> Result<InstantiateReceipt<u64>, &'static str> {
|
||||
input_data: Vec<u8>,
|
||||
) -> Result<(u64, ExecReturnValue), ExecError> {
|
||||
(**self).instantiate(code, value, gas_meter, input_data)
|
||||
}
|
||||
fn call(
|
||||
@@ -346,10 +328,9 @@ mod tests {
|
||||
to: &u64,
|
||||
value: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
input_data: &[u8],
|
||||
empty_output_buf: EmptyOutputBuf
|
||||
) -> Result<CallReceipt, &'static str> {
|
||||
(**self).call(to, value, gas_meter, input_data, empty_output_buf)
|
||||
input_data: Vec<u8>,
|
||||
) -> ExecResult {
|
||||
(**self).call(to, value, gas_meter, input_data)
|
||||
}
|
||||
fn note_dispatch_call(&mut self, call: Call) {
|
||||
(**self).note_dispatch_call(call)
|
||||
@@ -405,11 +386,10 @@ mod tests {
|
||||
|
||||
fn execute<E: Ext>(
|
||||
wat: &str,
|
||||
input_data: &[u8],
|
||||
output_data: &mut Vec<u8>,
|
||||
input_data: Vec<u8>,
|
||||
ext: E,
|
||||
gas_meter: &mut GasMeter<E::T>,
|
||||
) -> Result<(), &'static str> {
|
||||
) -> ExecResult {
|
||||
use crate::exec::Vm;
|
||||
|
||||
let wasm = wabt::wat2wasm(wat).unwrap();
|
||||
@@ -426,11 +406,7 @@ mod tests {
|
||||
let cfg = Default::default();
|
||||
let vm = WasmVm::new(&cfg);
|
||||
|
||||
*output_data = vm
|
||||
.execute(&exec, ext, input_data, EmptyOutputBuf::new(), gas_meter)
|
||||
.into_result()?;
|
||||
|
||||
Ok(())
|
||||
vm.execute(&exec, ext, input_data, gas_meter)
|
||||
}
|
||||
|
||||
const CODE_TRANSFER: &str = r#"
|
||||
@@ -475,14 +451,12 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_transfer() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_TRANSFER,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.transfers,
|
||||
@@ -539,14 +513,12 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_create() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_CREATE,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.creates,
|
||||
@@ -601,14 +573,12 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_call_limited_gas() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
execute(
|
||||
let _ = execute(
|
||||
&CODE_TRANSFER_LIMITED_GAS,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.transfers,
|
||||
@@ -625,7 +595,7 @@ mod tests {
|
||||
(module
|
||||
(import "env" "ext_get_storage" (func $ext_get_storage (param i32) (result i32)))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
@@ -661,7 +631,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; Copy scratch buffer into this contract memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 36) ;; The pointer where to store the scratch buffer contents,
|
||||
;; 36 = 4 + 32
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
@@ -696,17 +666,14 @@ mod tests {
|
||||
.storage
|
||||
.insert([0x11; 32], [0x22; 32].to_vec());
|
||||
|
||||
let mut return_buf = Vec::new();
|
||||
execute(
|
||||
let output = execute(
|
||||
CODE_GET_STORAGE,
|
||||
&[],
|
||||
&mut return_buf,
|
||||
vec![],
|
||||
mock_ext,
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(return_buf, [0x22; 32].to_vec());
|
||||
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: [0x22; 32].to_vec() });
|
||||
}
|
||||
|
||||
/// calls `ext_caller`, loads the address from the scratch buffer and
|
||||
@@ -715,7 +682,7 @@ mod tests {
|
||||
(module
|
||||
(import "env" "ext_caller" (func $ext_caller))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
@@ -740,7 +707,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
@@ -763,14 +730,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn caller() {
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_CALLER,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
/// calls `ext_address`, loads the address from the scratch buffer and
|
||||
@@ -779,7 +744,7 @@ mod tests {
|
||||
(module
|
||||
(import "env" "ext_address" (func $ext_address))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
@@ -804,7 +769,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
@@ -827,21 +792,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn address() {
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_ADDRESS,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
const CODE_BALANCE: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_balance" (func $ext_balance))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
@@ -866,7 +829,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
@@ -889,21 +852,19 @@ mod tests {
|
||||
#[test]
|
||||
fn balance() {
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_BALANCE,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
const CODE_GAS_PRICE: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_gas_price" (func $ext_gas_price))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
@@ -928,7 +889,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
@@ -951,21 +912,19 @@ mod tests {
|
||||
#[test]
|
||||
fn gas_price() {
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1312);
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_GAS_PRICE,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
const CODE_GAS_LEFT: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_gas_left" (func $ext_gas_left))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
@@ -991,7 +950,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
@@ -1012,17 +971,14 @@ mod tests {
|
||||
fn gas_left() {
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1312);
|
||||
|
||||
let mut return_buf = Vec::new();
|
||||
execute(
|
||||
let output = execute(
|
||||
CODE_GAS_LEFT,
|
||||
&[],
|
||||
&mut return_buf,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
let gas_left = Gas::decode(&mut &return_buf[..]).unwrap();
|
||||
let gas_left = Gas::decode(&mut output.data.as_slice()).unwrap();
|
||||
assert!(gas_left < 50_000, "gas_left must be less than initial");
|
||||
assert!(gas_left > gas_meter.gas_left(), "gas_left must be greater than final");
|
||||
}
|
||||
@@ -1031,7 +987,7 @@ mod tests {
|
||||
(module
|
||||
(import "env" "ext_value_transferred" (func $ext_value_transferred))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
@@ -1056,7 +1012,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
@@ -1079,14 +1035,12 @@ mod tests {
|
||||
#[test]
|
||||
fn value_transferred() {
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_VALUE_TRANSFERRED,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
const CODE_DISPATCH_CALL: &str = r#"
|
||||
@@ -1112,14 +1066,12 @@ mod tests {
|
||||
// let's rewrite so as we use this module controlled call or we serialize it in runtime.
|
||||
|
||||
let mut mock_ext = MockExt::default();
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_DISPATCH_CALL,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.dispatches,
|
||||
@@ -1154,24 +1106,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn return_from_start_fn() {
|
||||
let mut output_data = Vec::new();
|
||||
execute(
|
||||
let output = execute(
|
||||
CODE_RETURN_FROM_START_FN,
|
||||
&[],
|
||||
&mut output_data,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(output_data, vec![1, 2, 3, 4]);
|
||||
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: vec![1, 2, 3, 4] });
|
||||
}
|
||||
|
||||
const CODE_TIMESTAMP_NOW: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_now" (func $ext_now))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
@@ -1196,7 +1145,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
@@ -1219,21 +1168,19 @@ mod tests {
|
||||
#[test]
|
||||
fn now() {
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_TIMESTAMP_NOW,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
const CODE_RANDOM: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_random" (func $ext_random (param i32 i32)))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
@@ -1262,7 +1209,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 32) ;; Count of bytes to copy.
|
||||
@@ -1290,20 +1237,20 @@ mod tests {
|
||||
fn random() {
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
|
||||
let mut return_buf = Vec::new();
|
||||
execute(
|
||||
let output = execute(
|
||||
CODE_RANDOM,
|
||||
&[],
|
||||
&mut return_buf,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter,
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
// The mock ext just returns the same data that was passed as the subject.
|
||||
assert_eq!(
|
||||
&return_buf,
|
||||
&hex!("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F")
|
||||
output,
|
||||
ExecReturnValue {
|
||||
status: STATUS_SUCCESS,
|
||||
data: hex!("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F").to_vec(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1334,14 +1281,12 @@ mod tests {
|
||||
fn deposit_event() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_DEPOSIT_EVENT,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut gas_meter
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(mock_ext.events, vec![
|
||||
(vec![H256::repeat_byte(0x33)],
|
||||
@@ -1387,15 +1332,14 @@ mod tests {
|
||||
// Checks that the runtime traps if there are more than `max_topic_events` topics.
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
|
||||
assert_eq!(
|
||||
assert_matches!(
|
||||
execute(
|
||||
CODE_DEPOSIT_EVENT_MAX_TOPICS,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter
|
||||
),
|
||||
Err("during execution"),
|
||||
Err(ExecError { reason: "during execution", buffer: _ })
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1430,15 +1374,14 @@ mod tests {
|
||||
// Checks that the runtime traps if there are duplicates.
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
|
||||
assert_eq!(
|
||||
assert_matches!(
|
||||
execute(
|
||||
CODE_DEPOSIT_EVENT_DUPLICATES,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut gas_meter
|
||||
),
|
||||
Err("during execution"),
|
||||
Err(ExecError { reason: "during execution", buffer: _ })
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1448,7 +1391,7 @@ mod tests {
|
||||
(module
|
||||
(import "env" "ext_block_number" (func $ext_block_number))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
@@ -1473,7 +1416,7 @@ mod tests {
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(call $ext_scratch_read
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
@@ -1496,14 +1439,137 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn block_number() {
|
||||
execute(
|
||||
let _ = execute(
|
||||
CODE_BLOCK_NUMBER,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
)
|
||||
.unwrap();
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
// asserts that the size of the input data is 4.
|
||||
const CODE_SIMPLE_ASSERT: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
|
||||
(func $assert (param i32)
|
||||
(block $ok
|
||||
(br_if $ok
|
||||
(get_local 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
(func (export "call")
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_scratch_size)
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn output_buffer_capacity_preserved_on_success() {
|
||||
let mut input_data = Vec::with_capacity(1_234);
|
||||
input_data.extend_from_slice(&[1, 2, 3, 4][..]);
|
||||
|
||||
let output = execute(
|
||||
CODE_SIMPLE_ASSERT,
|
||||
input_data,
|
||||
MockExt::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(output.data.len(), 0);
|
||||
assert_eq!(output.data.capacity(), 1_234);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_buffer_capacity_preserved_on_failure() {
|
||||
let mut input_data = Vec::with_capacity(1_234);
|
||||
input_data.extend_from_slice(&[1, 2, 3, 4, 5][..]);
|
||||
|
||||
let error = execute(
|
||||
CODE_SIMPLE_ASSERT,
|
||||
input_data,
|
||||
MockExt::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
).err().unwrap();
|
||||
|
||||
assert_eq!(error.buffer.capacity(), 1_234);
|
||||
}
|
||||
|
||||
const CODE_RETURN_WITH_DATA: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_write" (func $ext_scratch_write (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; Deploy routine is the same as call.
|
||||
(func (export "deploy") (result i32)
|
||||
(call $call)
|
||||
)
|
||||
|
||||
;; Call reads the first 4 bytes (LE) as the exit status and returns the rest as output data.
|
||||
(func $call (export "call") (result i32)
|
||||
(local $buf_size i32)
|
||||
(local $exit_status i32)
|
||||
|
||||
;; Find out the size of the scratch buffer
|
||||
(set_local $buf_size (call $ext_scratch_size))
|
||||
|
||||
;; Copy scratch buffer into this contract memory.
|
||||
(call $ext_scratch_read
|
||||
(i32.const 0) ;; The pointer where to store the scratch buffer contents,
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(get_local $buf_size) ;; Count of bytes to copy.
|
||||
)
|
||||
|
||||
;; Copy all but the first 4 bytes of the input data as the output data.
|
||||
(call $ext_scratch_write
|
||||
(i32.const 4) ;; Offset from the start of the scratch buffer.
|
||||
(i32.sub ;; Count of bytes to copy.
|
||||
(get_local $buf_size)
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
|
||||
;; Return the first 4 bytes of the input data as the exit status.
|
||||
(i32.load (i32.const 0))
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn return_with_success_status() {
|
||||
let output = execute(
|
||||
CODE_RETURN_WITH_DATA,
|
||||
hex!("00112233445566778899").to_vec(),
|
||||
MockExt::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(output, ExecReturnValue { status: 0, data: hex!("445566778899").to_vec() });
|
||||
assert!(output.is_success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_with_failure_status() {
|
||||
let output = execute(
|
||||
CODE_RETURN_WITH_DATA,
|
||||
hex!("112233445566778899").to_vec(),
|
||||
MockExt::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(output, ExecReturnValue { status: 17, data: hex!("5566778899").to_vec() });
|
||||
assert!(!output.is_success());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user