mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 21:51:06 +00:00
implement contract events (#2161)
* 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 58ec010c0f4f4f0e16935ad41da32aedd17a8c57. * update test * Revert "update test" This reverts commit 6fe61a593ccf0d41f09a0b97472b28ed8751a999. * Revert "Revert "add deposit event base cost"" This reverts commit 145e8a9bac15313a4c380aa66b94fd4d36fa3f6d. * Fix format a bit
This commit is contained in:
@@ -59,8 +59,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
|||||||
spec_name: create_runtime_str!("node"),
|
spec_name: create_runtime_str!("node"),
|
||||||
impl_name: create_runtime_str!("substrate-node"),
|
impl_name: create_runtime_str!("substrate-node"),
|
||||||
authoring_version: 10,
|
authoring_version: 10,
|
||||||
spec_version: 56,
|
spec_version: 57,
|
||||||
impl_version: 60,
|
impl_version: 57,
|
||||||
apis: RUNTIME_API_VERSIONS,
|
apis: RUNTIME_API_VERSIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
**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
|
## ext_caller
|
||||||
|
|
||||||
This function serializes the address of the caller into the scratch buffer.
|
This function serializes the address of the caller into the scratch buffer.
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ pub trait Ext {
|
|||||||
/// Returns a reference to the account id of the current contract.
|
/// Returns a reference to the account id of the current contract.
|
||||||
fn address(&self) -> &AccountIdOf<Self::T>;
|
fn address(&self) -> &AccountIdOf<Self::T>;
|
||||||
|
|
||||||
|
|
||||||
/// Returns the balance of the current contract.
|
/// Returns the balance of the current contract.
|
||||||
///
|
///
|
||||||
/// The `value_transferred` is already added.
|
/// The `value_transferred` is already added.
|
||||||
@@ -103,6 +102,9 @@ pub trait Ext {
|
|||||||
|
|
||||||
/// Returns a reference to the random seed for the current block
|
/// Returns a reference to the random seed for the current block
|
||||||
fn random_seed(&self) -> &SeedOf<Self::T>;
|
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
|
/// Loader is a companion of the `Vm` trait. It loads an appropriate abstract
|
||||||
@@ -617,6 +619,10 @@ where
|
|||||||
fn now(&self) -> &T::Moment {
|
fn now(&self) -> &T::Moment {
|
||||||
&self.timestamp
|
&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.
|
/// These tests exercise the executive layer.
|
||||||
|
|||||||
@@ -405,6 +405,9 @@ decl_event! {
|
|||||||
/// A call was dispatched from the given account. The bool signals whether it was
|
/// A call was dispatched from the given account. The bool signals whether it was
|
||||||
/// successful execution or not.
|
/// successful execution or not.
|
||||||
Dispatched(AccountId, bool),
|
Dispatched(AccountId, bool),
|
||||||
|
|
||||||
|
/// An event from contract of account.
|
||||||
|
Contract(AccountId, Vec<u8>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,6 +506,12 @@ pub struct Schedule<Gas> {
|
|||||||
/// Gas cost per one byte returned.
|
/// Gas cost per one byte returned.
|
||||||
pub return_data_per_byte_cost: Gas,
|
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.
|
/// Gas cost per one byte read from the sandbox memory.
|
||||||
pub sandbox_data_read_cost: Gas,
|
pub sandbox_data_read_cost: Gas,
|
||||||
|
|
||||||
@@ -528,6 +537,8 @@ impl<Gas: As<u64>> Default for Schedule<Gas> {
|
|||||||
grow_mem_cost: Gas::sa(1),
|
grow_mem_cost: Gas::sa(1),
|
||||||
regular_op_cost: Gas::sa(1),
|
regular_op_cost: Gas::sa(1),
|
||||||
return_data_per_byte_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_read_cost: Gas::sa(1),
|
||||||
sandbox_data_write_cost: Gas::sa(1),
|
sandbox_data_write_cost: Gas::sa(1),
|
||||||
max_stack_height: 64 * 1024,
|
max_stack_height: 64 * 1024,
|
||||||
|
|||||||
@@ -291,10 +291,15 @@ fn account_removal_removes_storage() {
|
|||||||
const CODE_RETURN_FROM_START_FN: &str = r#"
|
const CODE_RETURN_FROM_START_FN: &str = r#"
|
||||||
(module
|
(module
|
||||||
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
(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))
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
(start $start)
|
(start $start)
|
||||||
(func $start
|
(func $start
|
||||||
|
(call $ext_deposit_event
|
||||||
|
(i32.const 8)
|
||||||
|
(i32.const 4)
|
||||||
|
)
|
||||||
(call $ext_return
|
(call $ext_return
|
||||||
(i32.const 8)
|
(i32.const 8)
|
||||||
(i32.const 4)
|
(i32.const 4)
|
||||||
@@ -310,10 +315,10 @@ const CODE_RETURN_FROM_START_FN: &str = r#"
|
|||||||
(data (i32.const 8) "\01\02\03\04")
|
(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]
|
#[test]
|
||||||
fn instantiate_and_call() {
|
fn instantiate_and_call_and_deposit_event() {
|
||||||
let wasm = wabt::wat2wasm(CODE_RETURN_FROM_START_FN).unwrap();
|
let wasm = wabt::wat2wasm(CODE_RETURN_FROM_START_FN).unwrap();
|
||||||
|
|
||||||
with_externalities(
|
with_externalities(
|
||||||
@@ -327,13 +332,14 @@ fn instantiate_and_call() {
|
|||||||
wasm,
|
wasm,
|
||||||
));
|
));
|
||||||
|
|
||||||
assert_ok!(Contract::create(
|
// Check at the end to get hash on error easily
|
||||||
|
let creation = Contract::create(
|
||||||
Origin::signed(ALICE),
|
Origin::signed(ALICE),
|
||||||
100,
|
100,
|
||||||
100_000,
|
100_000,
|
||||||
HASH_RETURN_FROM_START_FN.into(),
|
HASH_RETURN_FROM_START_FN.into(),
|
||||||
vec![],
|
vec![],
|
||||||
));
|
);
|
||||||
|
|
||||||
assert_eq!(System::events(), vec![
|
assert_eq!(System::events(), vec![
|
||||||
EventRecord {
|
EventRecord {
|
||||||
@@ -354,12 +360,17 @@ fn instantiate_and_call() {
|
|||||||
phase: Phase::ApplyExtrinsic(0),
|
phase: Phase::ApplyExtrinsic(0),
|
||||||
event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100))
|
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 {
|
EventRecord {
|
||||||
phase: Phase::ApplyExtrinsic(0),
|
phase: Phase::ApplyExtrinsic(0),
|
||||||
event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB))
|
event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB))
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
assert_ok!(creation);
|
||||||
assert!(AccountInfoOf::<Test>::exists(BOB));
|
assert!(AccountInfoOf::<Test>::exists(BOB));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ mod tests {
|
|||||||
creates: Vec<CreateEntry>,
|
creates: Vec<CreateEntry>,
|
||||||
transfers: Vec<TransferEntry>,
|
transfers: Vec<TransferEntry>,
|
||||||
dispatches: Vec<DispatchEntry>,
|
dispatches: Vec<DispatchEntry>,
|
||||||
|
events: Vec<Vec<u8>>,
|
||||||
next_account_id: u64,
|
next_account_id: u64,
|
||||||
random_seed: H256,
|
random_seed: H256,
|
||||||
}
|
}
|
||||||
@@ -276,6 +277,10 @@ mod tests {
|
|||||||
fn random_seed(&self) -> &H256{
|
fn random_seed(&self) -> &H256{
|
||||||
&self.random_seed
|
&self.random_seed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deposit_event(&mut self, data: Vec<u8>) {
|
||||||
|
self.events.push(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute<E: Ext>(
|
fn execute<E: Ext>(
|
||||||
@@ -1166,4 +1171,43 @@ mod tests {
|
|||||||
.unwrap();
|
.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]]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use system;
|
|||||||
use rstd::prelude::*;
|
use rstd::prelude::*;
|
||||||
use rstd::mem;
|
use rstd::mem;
|
||||||
use parity_codec::{Decode, Encode};
|
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.
|
/// Enumerates all possible *special* trap conditions.
|
||||||
///
|
///
|
||||||
@@ -108,6 +108,9 @@ pub enum RuntimeToken<Gas> {
|
|||||||
ReturnData(u32),
|
ReturnData(u32),
|
||||||
/// Dispatch fee calculated by `T::ComputeDispatchFee`.
|
/// Dispatch fee calculated by `T::ComputeDispatchFee`.
|
||||||
ComputedDispatchFee(Gas),
|
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> {
|
impl<T: Trait> Token<T> for RuntimeToken<T::Gas> {
|
||||||
@@ -126,6 +129,10 @@ impl<T: Trait> Token<T> for RuntimeToken<T::Gas> {
|
|||||||
ReturnData(byte_count) => metadata
|
ReturnData(byte_count) => metadata
|
||||||
.return_data_per_byte_cost
|
.return_data_per_byte_cost
|
||||||
.checked_mul(&<T::Gas as As<u32>>::sa(byte_count)),
|
.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),
|
ComputedDispatchFee(gas) => Some(gas),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -582,4 +589,23 @@ define_env!(Env, <E: Ext>,
|
|||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -435,8 +435,6 @@ fn storage_instance_independance() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO TODO: check configuration doublemapstorage in instances
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn storage_with_instance_basic_operation() {
|
fn storage_with_instance_basic_operation() {
|
||||||
with_externalities(&mut new_test_ext(), || {
|
with_externalities(&mut new_test_ext(), || {
|
||||||
|
|||||||
Reference in New Issue
Block a user