diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index e6d7f49020..79b0124f13 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -59,8 +59,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 56, - impl_version: 60, + spec_version: 57, + impl_version: 57, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/srml/contract/COMPLEXITY.md b/substrate/srml/contract/COMPLEXITY.md index 219d9244dd..9b304055f7 100644 --- a/substrate/srml/contract/COMPLEXITY.md +++ b/substrate/srml/contract/COMPLEXITY.md @@ -275,6 +275,17 @@ This function receives a `data` buffer as an argument. Execution of the function **complexity**: The complexity of this function is proportional to the size of the `data` buffer. +## ext_deposit_event + +This function receives a `data` buffer as an argument. Execution of the function consists of the following steps: + +1. Loading `data` buffer from the sandbox memory (see sandboxing memory get), +2. Insert to nested context execution +3. Copies from nested to underlying contexts +4. Call system deposit event + +**complexity**: The complexity of this function is proportional to the size of the `data` buffer. + ## ext_caller This function serializes the address of the caller into the scratch buffer. diff --git a/substrate/srml/contract/src/exec.rs b/substrate/srml/contract/src/exec.rs index 992dc02b0d..e56182947a 100644 --- a/substrate/srml/contract/src/exec.rs +++ b/substrate/srml/contract/src/exec.rs @@ -89,7 +89,6 @@ pub trait Ext { /// Returns a reference to the account id of the current contract. fn address(&self) -> &AccountIdOf; - /// Returns the balance of the current contract. /// /// The `value_transferred` is already added. @@ -103,6 +102,9 @@ pub trait Ext { /// Returns a reference to the random seed for the current block fn random_seed(&self) -> &SeedOf; + + /// Deposit an event. + fn deposit_event(&mut self, data: Vec); } /// Loader is a companion of the `Vm` trait. It loads an appropriate abstract @@ -617,6 +619,10 @@ where fn now(&self) -> &T::Moment { &self.timestamp } + + fn deposit_event(&mut self, data: Vec) { + self.ctx.events.push(RawEvent::Contract(self.ctx.self_account.clone(), data)); + } } /// These tests exercise the executive layer. diff --git a/substrate/srml/contract/src/lib.rs b/substrate/srml/contract/src/lib.rs index 59347bbabc..c919439d54 100644 --- a/substrate/srml/contract/src/lib.rs +++ b/substrate/srml/contract/src/lib.rs @@ -405,6 +405,9 @@ decl_event! { /// A call was dispatched from the given account. The bool signals whether it was /// successful execution or not. Dispatched(AccountId, bool), + + /// An event from contract of account. + Contract(AccountId, Vec), } } @@ -503,6 +506,12 @@ pub struct Schedule { /// Gas cost per one byte returned. pub return_data_per_byte_cost: Gas, + /// Gas cost to deposit an event; the per-byte portion. + pub event_data_per_byte_cost: Gas, + + /// Gas cost to deposit an event; the base. + pub event_data_base_cost: Gas, + /// Gas cost per one byte read from the sandbox memory. pub sandbox_data_read_cost: Gas, @@ -528,6 +537,8 @@ impl> Default for Schedule { grow_mem_cost: Gas::sa(1), regular_op_cost: Gas::sa(1), return_data_per_byte_cost: Gas::sa(1), + event_data_per_byte_cost: Gas::sa(1), + event_data_base_cost: Gas::sa(1), sandbox_data_read_cost: Gas::sa(1), sandbox_data_write_cost: Gas::sa(1), max_stack_height: 64 * 1024, diff --git a/substrate/srml/contract/src/tests.rs b/substrate/srml/contract/src/tests.rs index d26064ae0a..2f80023322 100644 --- a/substrate/srml/contract/src/tests.rs +++ b/substrate/srml/contract/src/tests.rs @@ -291,10 +291,15 @@ fn account_removal_removes_storage() { const CODE_RETURN_FROM_START_FN: &str = r#" (module (import "env" "ext_return" (func $ext_return (param i32 i32))) + (import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32))) (import "env" "memory" (memory 1 1)) (start $start) (func $start + (call $ext_deposit_event + (i32.const 8) + (i32.const 4) + ) (call $ext_return (i32.const 8) (i32.const 4) @@ -310,10 +315,10 @@ const CODE_RETURN_FROM_START_FN: &str = r#" (data (i32.const 8) "\01\02\03\04") ) "#; -const HASH_RETURN_FROM_START_FN: [u8; 32] = hex!("e6411d12daa2a19e4e9c7d8306c31c7d53a352cb8ed84385c8a1d48fc232e708"); +const HASH_RETURN_FROM_START_FN: [u8; 32] = hex!("abb4194bdea47b2904fe90b4fd674bd40d96f423956627df8c39d2b1a791ab9d"); #[test] -fn instantiate_and_call() { +fn instantiate_and_call_and_deposit_event() { let wasm = wabt::wat2wasm(CODE_RETURN_FROM_START_FN).unwrap(); with_externalities( @@ -327,13 +332,14 @@ fn instantiate_and_call() { wasm, )); - assert_ok!(Contract::create( + // Check at the end to get hash on error easily + let creation = Contract::create( Origin::signed(ALICE), 100, 100_000, HASH_RETURN_FROM_START_FN.into(), vec![], - )); + ); assert_eq!(System::events(), vec![ EventRecord { @@ -354,12 +360,17 @@ fn instantiate_and_call() { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)) }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Contract(BOB, vec![1, 2, 3, 4])) + }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)) } ]); + assert_ok!(creation); assert!(AccountInfoOf::::exists(BOB)); }, ); diff --git a/substrate/srml/contract/src/wasm/mod.rs b/substrate/srml/contract/src/wasm/mod.rs index 04428280d0..73ed859c1b 100644 --- a/substrate/srml/contract/src/wasm/mod.rs +++ b/substrate/srml/contract/src/wasm/mod.rs @@ -203,6 +203,7 @@ mod tests { creates: Vec, transfers: Vec, dispatches: Vec, + events: Vec>, next_account_id: u64, random_seed: H256, } @@ -276,6 +277,10 @@ mod tests { fn random_seed(&self) -> &H256{ &self.random_seed } + + fn deposit_event(&mut self, data: Vec) { + self.events.push(data) + } } fn execute( @@ -1166,4 +1171,43 @@ mod tests { .unwrap(); } + const CODE_DEPOSIT_EVENT: &str = r#" +(module + (import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32))) + (import "env" "memory" (memory 1 1)) + + (func (export "call") + (call $ext_deposit_event + (i32.const 8) ;; Pointer to the start of encoded call buffer + (i32.const 13) ;; Length of the buffer + ) + ) + (func (export "deploy")) + + (data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00") +) +"#; + + #[test] + fn deposit_event() { + // This test can fail due to the encoding changes. In case it becomes too annoying + // let's rewrite so as we use this module controlled call or we serialize it in runtime. + + let mut mock_ext = MockExt::default(); + let mut gas_meter = GasMeter::with_limit(50_000, 1); + execute( + CODE_DEPOSIT_EVENT, + &[], + &mut Vec::new(), + &mut mock_ext, + &mut gas_meter + ) + .unwrap(); + assert_eq!(gas_meter.gas_left(), 50_000 + - 4 // Explicit + - 13 - 1 // Deposit event + - 13 // read memory + ); + assert_eq!(mock_ext.events, vec![vec![0, 1, 42, 0, 0, 0, 0, 0, 0, 0, 229, 20, 0]]); + } } diff --git a/substrate/srml/contract/src/wasm/runtime.rs b/substrate/srml/contract/src/wasm/runtime.rs index b4a963f931..d68fbc2d53 100644 --- a/substrate/srml/contract/src/wasm/runtime.rs +++ b/substrate/srml/contract/src/wasm/runtime.rs @@ -24,7 +24,7 @@ use system; use rstd::prelude::*; use rstd::mem; use parity_codec::{Decode, Encode}; -use runtime_primitives::traits::{As, CheckedMul, Bounded}; +use runtime_primitives::traits::{As, CheckedMul, CheckedAdd, Bounded}; /// Enumerates all possible *special* trap conditions. /// @@ -108,6 +108,9 @@ pub enum RuntimeToken { ReturnData(u32), /// Dispatch fee calculated by `T::ComputeDispatchFee`. ComputedDispatchFee(Gas), + /// The given number of bytes is read from the sandbox memory and + /// deposit in as an event. + DepositEvent(u32), } impl Token for RuntimeToken { @@ -126,6 +129,10 @@ impl Token for RuntimeToken { ReturnData(byte_count) => metadata .return_data_per_byte_cost .checked_mul(&>::sa(byte_count)), + DepositEvent(byte_count) => metadata + .event_data_per_byte_cost + .checked_mul(&>::sa(byte_count)) + .and_then(|e| e.checked_add(&metadata.event_data_base_cost)), ComputedDispatchFee(gas) => Some(gas), }; @@ -582,4 +589,23 @@ define_env!(Env, , Ok(()) }, + + // Deposit a contract event with the data buffer. + ext_deposit_event(ctx, data_ptr: u32, data_len: u32) => { + match ctx + .gas_meter + .charge( + ctx.schedule, + RuntimeToken::DepositEvent(data_len) + ) + { + GasMeterResult::Proceed => (), + GasMeterResult::OutOfGas => return Err(sandbox::HostError), + } + + let event_data = read_sandbox_memory(ctx, data_ptr, data_len)?; + ctx.ext.deposit_event(event_data); + + Ok(()) + }, ); diff --git a/substrate/srml/support/test/tests/instance.rs b/substrate/srml/support/test/tests/instance.rs index 28ad3ee006..d5171f6ac7 100644 --- a/substrate/srml/support/test/tests/instance.rs +++ b/substrate/srml/support/test/tests/instance.rs @@ -435,8 +435,6 @@ fn storage_instance_independance() { }); } -// TODO TODO: check configuration doublemapstorage in instances - #[test] fn storage_with_instance_basic_operation() { with_externalities(&mut new_test_ext(), || {