mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-01 14:47:55 +00:00
Pass topics along with events (#2563)
* Introduce an IndexedEvent * Plumb topics through the Ext interface. * Add topics to ext_deposit_event * Charging for events. * Check the number of topics. * Check for duplicate topics. * Bump API version. * Move derive(*Eq) under test. * Use sorting for finding duplicates.
This commit is contained in:
committed by
Gavin Wood
parent
6ccdbea01c
commit
d57f6f9305
@@ -29,6 +29,9 @@ pub type CallOf<T> = <T as Trait>::Call;
|
||||
pub type MomentOf<T> = <T as timestamp::Trait>::Moment;
|
||||
pub type SeedOf<T> = <T as system::Trait>::Hash;
|
||||
|
||||
/// A type that represents a topic of an event. At the moment a hash is used.
|
||||
pub type TopicOf<T> = <T as system::Trait>::Hash;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct InstantiateReceipt<AccountId> {
|
||||
pub address: AccountId,
|
||||
@@ -106,8 +109,10 @@ 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>);
|
||||
/// Deposit an event with the given topics.
|
||||
///
|
||||
/// There should not be any duplicates in `topics`.
|
||||
fn deposit_event(&mut self, topics: Vec<TopicOf<Self::T>>, data: Vec<u8>);
|
||||
|
||||
/// Set rent allowance of the contract
|
||||
fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<Self::T>);
|
||||
@@ -189,6 +194,15 @@ impl VmExecResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct that records a request to deposit an event with a list of topics.
|
||||
#[cfg_attr(any(feature = "std", test), derive(Debug, PartialEq, Eq))]
|
||||
pub struct IndexedEvent<T: Trait> {
|
||||
/// A list of topics this event will be deposited with.
|
||||
pub topics: Vec<T::Hash>,
|
||||
/// The event to deposit.
|
||||
pub event: Event<T>,
|
||||
}
|
||||
|
||||
/// A trait that represent a virtual machine.
|
||||
///
|
||||
/// You can view a virtual machine as something that takes code, an input data buffer,
|
||||
@@ -238,7 +252,7 @@ pub struct ExecutionContext<'a, T: Trait + 'a, V, L> {
|
||||
pub self_trie_id: Option<TrieId>,
|
||||
pub overlay: OverlayAccountDb<'a, T>,
|
||||
pub depth: usize,
|
||||
pub events: Vec<Event<T>>,
|
||||
pub events: Vec<IndexedEvent<T>>,
|
||||
pub calls: Vec<(T::AccountId, T::Call)>,
|
||||
pub config: &'a Config<T>,
|
||||
pub vm: &'a V,
|
||||
@@ -418,7 +432,10 @@ where
|
||||
.into_result()?;
|
||||
|
||||
// Deposit an instantiation event.
|
||||
nested.events.push(RawEvent::Instantiated(self.self_account.clone(), dest.clone()));
|
||||
nested.events.push(IndexedEvent {
|
||||
event: RawEvent::Instantiated(self.self_account.clone(), dest.clone()),
|
||||
topics: Vec::new(),
|
||||
});
|
||||
|
||||
(nested.overlay.into_change_set(), nested.events, nested.calls)
|
||||
};
|
||||
@@ -545,8 +562,10 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
|
||||
if transactor != dest {
|
||||
ctx.overlay.set_balance(transactor, new_from_balance);
|
||||
ctx.overlay.set_balance(dest, new_to_balance);
|
||||
ctx.events
|
||||
.push(RawEvent::Transfer(transactor.clone(), dest.clone(), value));
|
||||
ctx.events.push(IndexedEvent {
|
||||
event: RawEvent::Transfer(transactor.clone(), dest.clone(), value),
|
||||
topics: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -631,8 +650,11 @@ where
|
||||
&self.timestamp
|
||||
}
|
||||
|
||||
fn deposit_event(&mut self, data: Vec<u8>) {
|
||||
self.ctx.events.push(RawEvent::Contract(self.ctx.self_account.clone(), data));
|
||||
fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {
|
||||
self.ctx.events.push(IndexedEvent {
|
||||
topics,
|
||||
event: RawEvent::Contract(self.ctx.self_account.clone(), data),
|
||||
});
|
||||
}
|
||||
|
||||
fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<T>) {
|
||||
@@ -659,7 +681,7 @@ where
|
||||
mod tests {
|
||||
use super::{
|
||||
BalanceOf, ExecFeeToken, ExecutionContext, Ext, Loader, EmptyOutputBuf, TransferFeeKind, TransferFeeToken,
|
||||
Vm, VmExecResult, InstantiateReceipt, RawEvent,
|
||||
Vm, VmExecResult, InstantiateReceipt, RawEvent, IndexedEvent,
|
||||
};
|
||||
use crate::account_db::AccountDb;
|
||||
use crate::gas::GasMeter;
|
||||
@@ -1262,8 +1284,14 @@ mod tests {
|
||||
// there are instantiation event.
|
||||
assert_eq!(ctx.overlay.get_code_hash(&created_contract_address).unwrap(), dummy_ch);
|
||||
assert_eq!(&ctx.events, &[
|
||||
RawEvent::Transfer(ALICE, created_contract_address, 100),
|
||||
RawEvent::Instantiated(ALICE, created_contract_address),
|
||||
IndexedEvent {
|
||||
event: RawEvent::Transfer(ALICE, created_contract_address, 100),
|
||||
topics: Vec::new(),
|
||||
},
|
||||
IndexedEvent {
|
||||
event: RawEvent::Instantiated(ALICE, created_contract_address),
|
||||
topics: Vec::new(),
|
||||
}
|
||||
]);
|
||||
}
|
||||
);
|
||||
@@ -1314,9 +1342,18 @@ mod tests {
|
||||
// there are instantiation event.
|
||||
assert_eq!(ctx.overlay.get_code_hash(&created_contract_address).unwrap(), dummy_ch);
|
||||
assert_eq!(&ctx.events, &[
|
||||
RawEvent::Transfer(ALICE, BOB, 20),
|
||||
RawEvent::Transfer(BOB, created_contract_address, 15),
|
||||
RawEvent::Instantiated(BOB, created_contract_address),
|
||||
IndexedEvent {
|
||||
event: RawEvent::Transfer(ALICE, BOB, 20),
|
||||
topics: Vec::new(),
|
||||
},
|
||||
IndexedEvent {
|
||||
event: RawEvent::Transfer(BOB, created_contract_address, 15),
|
||||
topics: Vec::new(),
|
||||
},
|
||||
IndexedEvent {
|
||||
event: RawEvent::Instantiated(BOB, created_contract_address),
|
||||
topics: Vec::new(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
@@ -1362,7 +1399,10 @@ mod tests {
|
||||
// The contract wasn't created so we don't expect to see an instantiation
|
||||
// event here.
|
||||
assert_eq!(&ctx.events, &[
|
||||
RawEvent::Transfer(ALICE, BOB, 20),
|
||||
IndexedEvent {
|
||||
event: RawEvent::Transfer(ALICE, BOB, 20),
|
||||
topics: Vec::new(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -393,7 +393,12 @@ decl_module! {
|
||||
DirectAccountDb.commit(ctx.overlay.into_change_set());
|
||||
|
||||
// Then deposit all events produced.
|
||||
ctx.events.into_iter().for_each(Self::deposit_event);
|
||||
ctx.events.into_iter().for_each(|indexed_event| {
|
||||
<system::Module<T>>::deposit_event_indexed(
|
||||
&*indexed_event.topics,
|
||||
<T as Trait>::Event::from(indexed_event.event).into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Refund cost of the unused gas.
|
||||
@@ -447,7 +452,12 @@ decl_module! {
|
||||
DirectAccountDb.commit(ctx.overlay.into_change_set());
|
||||
|
||||
// Then deposit all events produced.
|
||||
ctx.events.into_iter().for_each(Self::deposit_event);
|
||||
ctx.events.into_iter().for_each(|indexed_event| {
|
||||
<system::Module<T>>::deposit_event_indexed(
|
||||
&*indexed_event.topics,
|
||||
<T as Trait>::Event::from(indexed_event.event).into(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Refund cost of the unused gas.
|
||||
@@ -653,8 +663,11 @@ pub struct Schedule<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 cost per topic.
|
||||
pub event_per_topic_cost: Gas,
|
||||
|
||||
/// Gas cost to deposit an event; the base.
|
||||
pub event_data_base_cost: Gas,
|
||||
pub event_base_cost: Gas,
|
||||
|
||||
/// Gas cost per one byte read from the sandbox memory.
|
||||
pub sandbox_data_read_cost: Gas,
|
||||
@@ -662,6 +675,9 @@ pub struct Schedule<Gas> {
|
||||
/// Gas cost per one byte written to the sandbox memory.
|
||||
pub sandbox_data_write_cost: Gas,
|
||||
|
||||
/// The maximum number of topics supported by an event.
|
||||
pub max_event_topics: u32,
|
||||
|
||||
/// Maximum allowed stack height.
|
||||
///
|
||||
/// See https://wiki.parity.io/WebAssembly-StackHeight to find out
|
||||
@@ -685,9 +701,11 @@ impl<Gas: As<u64>> Default for Schedule<Gas> {
|
||||
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),
|
||||
event_per_topic_cost: Gas::sa(1),
|
||||
event_base_cost: Gas::sa(1),
|
||||
sandbox_data_read_cost: Gas::sa(1),
|
||||
sandbox_data_write_cost: Gas::sa(1),
|
||||
max_event_topics: 4,
|
||||
max_stack_height: 64 * 1024,
|
||||
max_memory_pages: 16,
|
||||
enable_println: false,
|
||||
|
||||
@@ -64,7 +64,7 @@ impl_outer_dispatch! {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct Test;
|
||||
impl system::Trait for Test {
|
||||
type Origin = Origin;
|
||||
@@ -313,14 +313,16 @@ 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" "ext_deposit_event" (func $ext_deposit_event (param i32 i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(start $start)
|
||||
(func $start
|
||||
(call $ext_deposit_event
|
||||
(i32.const 8)
|
||||
(i32.const 4)
|
||||
(i32.const 0) ;; The topics buffer
|
||||
(i32.const 0) ;; The topics buffer's length
|
||||
(i32.const 8) ;; The data buffer
|
||||
(i32.const 4) ;; The data buffer's length
|
||||
)
|
||||
(call $ext_return
|
||||
(i32.const 8)
|
||||
@@ -337,7 +339,7 @@ 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!("abb4194bdea47b2904fe90b4fd674bd40d96f423956627df8c39d2b1a791ab9d");
|
||||
const HASH_RETURN_FROM_START_FN: [u8; 32] = hex!("66c45bd7c473a1746e1d241176166ef53b1f207f56c5e87d1b6650140704181b");
|
||||
|
||||
#[test]
|
||||
fn instantiate_and_call_and_deposit_event() {
|
||||
|
||||
@@ -204,7 +204,8 @@ mod tests {
|
||||
creates: Vec<CreateEntry>,
|
||||
transfers: Vec<TransferEntry>,
|
||||
dispatches: Vec<DispatchEntry>,
|
||||
events: Vec<Vec<u8>>,
|
||||
// (topics, data)
|
||||
events: Vec<(Vec<H256>, Vec<u8>)>,
|
||||
next_account_id: u64,
|
||||
random_seed: H256,
|
||||
}
|
||||
@@ -279,8 +280,8 @@ mod tests {
|
||||
&self.random_seed
|
||||
}
|
||||
|
||||
fn deposit_event(&mut self, data: Vec<u8>) {
|
||||
self.events.push(data)
|
||||
fn deposit_event(&mut self, topics: Vec<H256>, data: Vec<u8>) {
|
||||
self.events.push((topics, data))
|
||||
}
|
||||
|
||||
fn set_rent_allowance(&mut self, rent_allowance: u64) {
|
||||
@@ -1182,26 +1183,29 @@ mod tests {
|
||||
|
||||
const CODE_DEPOSIT_EVENT: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32)))
|
||||
(import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32 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 32) ;; Pointer to the start of topics buffer
|
||||
(i32.const 33) ;; The length of the topics buffer.
|
||||
(i32.const 8) ;; Pointer to the start of the data 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")
|
||||
|
||||
;; Encoded Vec<TopicOf<T>>, the buffer has length of 33 bytes.
|
||||
(data (i32.const 32) "\04\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33\33"
|
||||
"\33\33\33\33\33\33\33\33\33")
|
||||
)
|
||||
"#;
|
||||
|
||||
#[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(
|
||||
@@ -1212,11 +1216,105 @@ mod tests {
|
||||
&mut gas_meter
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(mock_ext.events, vec![
|
||||
(vec![H256::repeat_byte(0x33)],
|
||||
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00])
|
||||
]);
|
||||
|
||||
assert_eq!(gas_meter.gas_left(), 50_000
|
||||
- 4 // Explicit
|
||||
- 13 - 1 // Deposit event
|
||||
- 13 // read memory
|
||||
- 6 // Explicit
|
||||
- 13 - 1 - 1 // Deposit event
|
||||
- (13 + 33) // read memory
|
||||
);
|
||||
}
|
||||
|
||||
const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func (export "call")
|
||||
(call $ext_deposit_event
|
||||
(i32.const 32) ;; Pointer to the start of topics buffer
|
||||
(i32.const 161) ;; The length of the topics buffer.
|
||||
(i32.const 8) ;; Pointer to the start of the data 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")
|
||||
|
||||
;; Encoded Vec<TopicOf<T>>, the buffer has length of 161 bytes.
|
||||
(data (i32.const 32) "\14"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02"
|
||||
"\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03\03"
|
||||
"\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04"
|
||||
"\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05\05")
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn deposit_event_max_topics() {
|
||||
// Checks that the runtime traps if there are more than `max_topic_events` topics.
|
||||
let mut mock_ext = MockExt::default();
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
|
||||
assert_eq!(
|
||||
execute(
|
||||
CODE_DEPOSIT_EVENT_MAX_TOPICS,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
&mut mock_ext,
|
||||
&mut gas_meter
|
||||
),
|
||||
Err("during execution"),
|
||||
);
|
||||
}
|
||||
|
||||
const CODE_DEPOSIT_EVENT_DUPLICATES: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_deposit_event" (func $ext_deposit_event (param i32 i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func (export "call")
|
||||
(call $ext_deposit_event
|
||||
(i32.const 32) ;; Pointer to the start of topics buffer
|
||||
(i32.const 129) ;; The length of the topics buffer.
|
||||
(i32.const 8) ;; Pointer to the start of the data 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")
|
||||
|
||||
;; Encoded Vec<TopicOf<T>>, the buffer has length of 129 bytes.
|
||||
(data (i32.const 32) "\10"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02"
|
||||
"\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01"
|
||||
"\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04")
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn deposit_event_duplicates() {
|
||||
// Checks that the runtime traps if there are duplicates.
|
||||
let mut mock_ext = MockExt::default();
|
||||
let mut gas_meter = GasMeter::with_limit(50_000, 1);
|
||||
|
||||
assert_eq!(
|
||||
execute(
|
||||
CODE_DEPOSIT_EVENT_DUPLICATES,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
&mut mock_ext,
|
||||
&mut gas_meter
|
||||
),
|
||||
Err("during execution"),
|
||||
);
|
||||
assert_eq!(mock_ext.events, vec![vec![0, 1, 42, 0, 0, 0, 0, 0, 0, 0, 229, 20, 0]]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
//! Environment definition of the wasm smart-contract runtime.
|
||||
|
||||
use crate::{Schedule, Trait, CodeHash, ComputeDispatchFee, BalanceOf};
|
||||
use crate::exec::{Ext, VmExecResult, OutputBuf, EmptyOutputBuf, CallReceipt, InstantiateReceipt, StorageKey};
|
||||
use crate::exec::{
|
||||
Ext, VmExecResult, OutputBuf, EmptyOutputBuf, CallReceipt, InstantiateReceipt, StorageKey,
|
||||
TopicOf,
|
||||
};
|
||||
use crate::gas::{GasMeter, Token, GasMeterResult, approx_gas_for_balance};
|
||||
use sandbox;
|
||||
use system;
|
||||
@@ -108,9 +111,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),
|
||||
/// (topic_count, data_bytes): A buffer of the given size is posted as an event indexed with the
|
||||
/// given number of topics.
|
||||
DepositEvent(u32, u32),
|
||||
}
|
||||
|
||||
impl<T: Trait> Token<T> for RuntimeToken<T::Gas> {
|
||||
@@ -129,10 +132,25 @@ 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)),
|
||||
DepositEvent(topic_count, data_byte_count) => {
|
||||
let data_cost = metadata
|
||||
.event_data_per_byte_cost
|
||||
.checked_mul(&<T::Gas as As<u32>>::sa(data_byte_count));
|
||||
|
||||
let topics_cost = metadata
|
||||
.event_per_topic_cost
|
||||
.checked_mul(&<T::Gas as As<u32>>::sa(topic_count));
|
||||
|
||||
data_cost
|
||||
.and_then(|data_cost| {
|
||||
topics_cost.and_then(|topics_cost| {
|
||||
data_cost.checked_add(&topics_cost)
|
||||
})
|
||||
})
|
||||
.and_then(|data_and_topics_cost|
|
||||
data_and_topics_cost.checked_add(&metadata.event_base_cost)
|
||||
)
|
||||
},
|
||||
ComputedDispatchFee(gas) => Some(gas),
|
||||
};
|
||||
|
||||
@@ -610,21 +628,47 @@ define_env!(Env, <E: Ext>,
|
||||
Ok(())
|
||||
},
|
||||
|
||||
// Deposit a contract event with the data buffer.
|
||||
ext_deposit_event(ctx, data_ptr: u32, data_len: u32) => {
|
||||
// Deposit a contract event with the data buffer and optional list of topics. There is a limit
|
||||
// on the maximum number of topics specified by `max_event_topics`.
|
||||
//
|
||||
// - topics_ptr - a pointer to the buffer of topics encoded as `Vec<T::Hash>`. The value of this
|
||||
// is ignored if `topics_len` is set to 0. The topics list can't contain duplicates.
|
||||
// - topics_len - the length of the topics buffer. Pass 0 if you want to pass an empty vector.
|
||||
// - data_ptr - a pointer to a raw data buffer which will saved along the event.
|
||||
// - data_len - the length of the data buffer.
|
||||
ext_deposit_event(ctx, topics_ptr: u32, topics_len: u32, data_ptr: u32, data_len: u32) => {
|
||||
let mut topics = match topics_len {
|
||||
0 => Vec::new(),
|
||||
_ => {
|
||||
let topics_buf = read_sandbox_memory(ctx, topics_ptr, topics_len)?;
|
||||
Vec::<TopicOf<<E as Ext>::T>>::decode(&mut &topics_buf[..])
|
||||
.ok_or_else(|| sandbox::HostError)?
|
||||
}
|
||||
};
|
||||
|
||||
// If there are more than `max_event_topics`, then trap.
|
||||
if topics.len() > ctx.schedule.max_event_topics as usize {
|
||||
return Err(sandbox::HostError);
|
||||
}
|
||||
|
||||
// Check for duplicate topics. If there are any, then trap.
|
||||
if has_duplicates(&mut topics) {
|
||||
return Err(sandbox::HostError);
|
||||
}
|
||||
|
||||
let event_data = read_sandbox_memory(ctx, data_ptr, data_len)?;
|
||||
|
||||
match ctx
|
||||
.gas_meter
|
||||
.charge(
|
||||
ctx.schedule,
|
||||
RuntimeToken::DepositEvent(data_len)
|
||||
RuntimeToken::DepositEvent(topics.len() as u32, 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);
|
||||
ctx.ext.deposit_event(topics, event_data);
|
||||
|
||||
Ok(())
|
||||
},
|
||||
@@ -665,3 +709,21 @@ define_env!(Env, <E: Ext>,
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
|
||||
/// Finds duplicates in a given vector.
|
||||
///
|
||||
/// This function has complexity of O(n log n) and no additional memory is required, although
|
||||
/// the order of items is not preserved.
|
||||
fn has_duplicates<T: PartialEq + AsRef<[u8]>>(items: &mut Vec<T>) -> bool {
|
||||
// Sort the vector
|
||||
items.sort_unstable_by(|a, b| {
|
||||
Ord::cmp(a.as_ref(), b.as_ref())
|
||||
});
|
||||
// And then find any two consecutive equal elements.
|
||||
items.windows(2).any(|w| {
|
||||
match w {
|
||||
&[ref a, ref b] => a == b,
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user