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

Commit

Permalink
implement contract events (#2161)
Browse files Browse the repository at this point in the history
* implement contract events

* update runtime

* renaming

* update test code hash

* improve complexity details

* add deposit event base cost

* add test

* Revert "add deposit event base cost"

This reverts commit 58ec010.

* update test

* Revert "update test"

This reverts commit 6fe61a5.

* Revert "Revert "add deposit event base cost""

This reverts commit 145e8a9.

* Fix format a bit
  • Loading branch information
thiolliere authored and gavofyork committed Apr 3, 2019
1 parent 95fa251 commit e748c33
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 10 deletions.
4 changes: 2 additions & 2 deletions node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down
11 changes: 11 additions & 0 deletions srml/contract/COMPLEXITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 7 additions & 1 deletion srml/contract/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ pub trait Ext {
/// Returns a reference to the account id of the current contract.
fn address(&self) -> &AccountIdOf<Self::T>;


/// Returns the balance of the current contract.
///
/// The `value_transferred` is already added.
Expand All @@ -103,6 +102,9 @@ pub trait Ext {

/// Returns a reference to the random seed for the current block
fn random_seed(&self) -> &SeedOf<Self::T>;

/// Deposit an event.
fn deposit_event(&mut self, data: Vec<u8>);
}

/// Loader is a companion of the `Vm` trait. It loads an appropriate abstract
Expand Down Expand Up @@ -617,6 +619,10 @@ where
fn now(&self) -> &T::Moment {
&self.timestamp
}

fn deposit_event(&mut self, data: Vec<u8>) {
self.ctx.events.push(RawEvent::Contract(self.ctx.self_account.clone(), data));
}
}

/// These tests exercise the executive layer.
Expand Down
11 changes: 11 additions & 0 deletions srml/contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>),
}
}

Expand Down Expand Up @@ -503,6 +506,12 @@ pub struct Schedule<Gas> {
/// 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,

Expand All @@ -528,6 +537,8 @@ impl<Gas: As<u64>> Default for Schedule<Gas> {
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,
Expand Down
19 changes: 15 additions & 4 deletions srml/contract/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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(
Expand All @@ -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 {
Expand All @@ -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::<Test>::exists(BOB));
},
);
Expand Down
44 changes: 44 additions & 0 deletions srml/contract/src/wasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ mod tests {
creates: Vec<CreateEntry>,
transfers: Vec<TransferEntry>,
dispatches: Vec<DispatchEntry>,
events: Vec<Vec<u8>>,
next_account_id: u64,
random_seed: H256,
}
Expand Down Expand Up @@ -276,6 +277,10 @@ mod tests {
fn random_seed(&self) -> &H256{
&self.random_seed
}

fn deposit_event(&mut self, data: Vec<u8>) {
self.events.push(data)
}
}

fn execute<E: Ext>(
Expand Down Expand Up @@ -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]]);
}
}
28 changes: 27 additions & 1 deletion srml/contract/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -108,6 +108,9 @@ pub enum RuntimeToken<Gas> {
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<T: Trait> Token<T> for RuntimeToken<T::Gas> {
Expand All @@ -126,6 +129,10 @@ impl<T: Trait> Token<T> for RuntimeToken<T::Gas> {
ReturnData(byte_count) => metadata
.return_data_per_byte_cost
.checked_mul(&<T::Gas as As<u32>>::sa(byte_count)),
DepositEvent(byte_count) => metadata
.event_data_per_byte_cost
.checked_mul(&<T::Gas as As<u32>>::sa(byte_count))
.and_then(|e| e.checked_add(&metadata.event_data_base_cost)),
ComputedDispatchFee(gas) => Some(gas),
};

Expand Down Expand Up @@ -582,4 +589,23 @@ define_env!(Env, <E: Ext>,

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(())
},
);
2 changes: 0 additions & 2 deletions srml/support/test/tests/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(), || {
Expand Down

0 comments on commit e748c33

Please sign in to comment.