Skip to content

Commit af42697

Browse files
fridrik01Stebalien
authored andcommitted
feat: Improved event syscall API (#1807)
This PR changes the internal emit_event syscall and removes the CBOR formatting made between SDK and FVM. It does so by splitting the ActorEvent entries manually into three buffers that we know the exact size of and allows us to perform validation of certain cases (e.g check for max values) before doing any parsing.
1 parent b344ed4 commit af42697

File tree

15 files changed

+289
-168
lines changed

15 files changed

+289
-168
lines changed

Cargo.lock

+22-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fvm/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ rand = "0.8.5"
3838
quickcheck = { version = "1", optional = true }
3939
once_cell = "1.18"
4040
minstant = "0.1.2"
41+
static_assertions = "1.1.0"
4142

4243
[dev-dependencies]
4344
pretty_assertions = "1.3.0"

fvm/src/gas/price_list.rs

+44-67
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use std::ops::Mul;
77

88
use anyhow::Context;
99
use fvm_shared::crypto::signature::SignatureType;
10-
use fvm_shared::event::{ActorEvent, Flags};
1110
use fvm_shared::piece::PieceInfo;
1211
use fvm_shared::sector::{
1312
AggregateSealVerifyProofAndInfos, RegisteredPoStProof, RegisteredSealProof, ReplicaUpdateInfo,
@@ -27,6 +26,21 @@ use crate::kernel::SupportedHashes;
2726
// https://docs.rs/wasmtime/2.0.2/wasmtime/struct.InstanceLimits.html#structfield.table_elements
2827
const TABLE_ELEMENT_SIZE: u32 = 8;
2928

29+
// The maximum overhead (in bytes) of a single event when encoded into CBOR.
30+
//
31+
// 1: CBOR tuple with 2 fields (StampedEvent)
32+
// 9: Emitter ID
33+
// 2: Entry array overhead (max size 255)
34+
const EVENT_OVERHEAD: u64 = 12;
35+
// The maximum overhead (in bytes) of a single event entry when encoded into CBOR.
36+
//
37+
// 1: CBOR tuple with 4 fields
38+
// 1: Flags (will adjust as more flags are added)
39+
// 2: Key major type + length (up to 255 bytes)
40+
// 2: Codec major type + value (codec should be <= 255)
41+
// 3: Value major type + length (up to 8192 bytes)
42+
const EVENT_ENTRY_OVERHEAD: u64 = 9;
43+
3044
/// Create a mapping from enum items to values in a way that guarantees at compile
3145
/// time that we did not miss any member, in any of the prices, even if the enum
3246
/// gets a new member later.
@@ -293,26 +307,15 @@ lazy_static! {
293307
memory_fill_per_byte_cost: Gas::from_milligas(400),
294308
},
295309

296-
// These parameters are specifically sized for EVM events. They will need
297-
// to be revisited before Wasm actors are able to emit arbitrary events.
298-
//
299-
// Validation costs are dependent on encoded length, but also
300-
// co-dependent on the number of entries. The latter is a chicken-and-egg
301-
// situation because we can only learn how many entries were emitted once we
302-
// decode the CBOR event.
303-
//
304-
// We will likely need to revisit the ABI of emit_event to remove CBOR
305-
// as the exchange format.
306-
event_validation_cost: ScalingCost {
310+
// TODO(#1817): Per-entry event validation cost. These parameters were benchmarked for the
311+
// EVM but haven't been revisited since revising the API.
312+
event_per_entry: ScalingCost {
307313
flat: Gas::new(1750),
308314
scale: Gas::new(25),
309315
},
310316

311-
// The protocol does not currently mandate indexing work, so these are
312-
// left at zero. Once we start populating and committing indexing data
313-
// structures, these costs will need to reflect the computation and
314-
// storage costs of such actions.
315-
event_accept_per_index_element: ScalingCost {
317+
// TODO(#1817): Cost of validating utf8 (used in event parsing).
318+
utf8_validation: ScalingCost {
316319
flat: Zero::zero(),
317320
scale: Zero::zero(),
318321
},
@@ -468,14 +471,14 @@ pub struct PriceList {
468471

469472
/// Gas cost to validate an ActorEvent as soon as it's received from the actor, and prior
470473
/// to it being parsed.
471-
pub(crate) event_validation_cost: ScalingCost,
472-
473-
/// Gas cost of every indexed element, scaling per number of bytes indexed.
474-
pub(crate) event_accept_per_index_element: ScalingCost,
474+
pub(crate) event_per_entry: ScalingCost,
475475

476476
/// Gas cost of doing lookups in the builtin actor mappings.
477477
pub(crate) builtin_actor_manifest_lookup: Gas,
478478

479+
/// Gas cost of utf8 parsing.
480+
pub(crate) utf8_validation: ScalingCost,
481+
479482
/// Gas cost of accessing the network context.
480483
pub(crate) network_context: Gas,
481484
/// Gas cost of accessing the message context.
@@ -921,59 +924,33 @@ impl PriceList {
921924
}
922925

923926
#[inline]
924-
pub fn on_actor_event_validate(&self, data_size: usize) -> GasCharge {
925-
let memcpy = self.block_memcpy.apply(data_size);
926-
let alloc = self.block_allocate.apply(data_size);
927-
let validate = self.event_validation_cost.apply(data_size);
928-
929-
GasCharge::new(
930-
"OnActorEventValidate",
931-
memcpy + alloc + validate,
932-
Zero::zero(),
933-
)
934-
}
935-
936-
#[inline]
937-
pub fn on_actor_event_accept(&self, evt: &ActorEvent, serialized_len: usize) -> GasCharge {
938-
let (mut indexed_bytes, mut indexed_elements) = (0usize, 0u32);
939-
for evt in evt.entries.iter() {
940-
if evt.flags.contains(Flags::FLAG_INDEXED_KEY) {
941-
indexed_bytes += evt.key.len();
942-
indexed_elements += 1;
943-
}
944-
if evt.flags.contains(Flags::FLAG_INDEXED_VALUE) {
945-
indexed_bytes += evt.value.len();
946-
indexed_elements += 1;
947-
}
948-
}
927+
pub fn on_actor_event(&self, entries: usize, keysize: usize, valuesize: usize) -> GasCharge {
928+
// Here we estimate per-event overhead given the constraints on event values.
949929

950-
// The estimated size of the serialized StampedEvent event, which
951-
// includes the ActorEvent + 8 bytes for the actor ID + some bytes
952-
// for CBOR framing.
953-
const STAMP_EXTRA_SIZE: usize = 12;
954-
let stamped_event_size = serialized_len + STAMP_EXTRA_SIZE;
930+
let validate_entries = self.event_per_entry.apply(entries);
931+
let validate_utf8 = self.utf8_validation.apply(keysize);
955932

956-
// Charge for 3 memory copy operations.
957-
// This includes the cost of forming a StampedEvent, copying into the
958-
// AMT's buffer on finish, and returning to the client.
959-
let memcpy = self.block_memcpy.apply(stamped_event_size);
933+
// Estimate the size, saturating at max-u64. Given how we calculate gas, this will saturate
934+
// the gas maximum at max-u64 milligas.
935+
let estimated_size = EVENT_OVERHEAD
936+
.saturating_add(EVENT_ENTRY_OVERHEAD.saturating_mul(entries as u64))
937+
.saturating_add(keysize as u64)
938+
.saturating_add(valuesize as u64);
960939

961-
// Charge for 2 memory allocations.
962-
// This includes the cost of retaining the StampedEvent in the call manager,
963-
// and allocaing into the AMT's buffer on finish.
964-
let alloc = self.block_allocate.apply(stamped_event_size);
940+
// Calculate the cost per copy (one memcpy + one allocation).
941+
let mem =
942+
self.block_memcpy.apply(estimated_size) + self.block_allocate.apply(estimated_size);
965943

966944
// Charge for the hashing on AMT insertion.
967-
let hash = self.hashing_cost[&SupportedHashes::Blake2b256].apply(stamped_event_size);
945+
let hash = self.hashing_cost[&SupportedHashes::Blake2b256].apply(estimated_size);
968946

969947
GasCharge::new(
970-
"OnActorEventAccept",
971-
memcpy + alloc,
972-
self.event_accept_per_index_element.flat * indexed_elements
973-
+ self.event_accept_per_index_element.scale * indexed_bytes
974-
+ memcpy * 2u32 // deferred cost, placing here to hide from benchmark
975-
+ alloc // deferred cost, placing here to hide from benchmark
976-
+ hash, // deferred cost, placing here to hide from benchmark
948+
"OnActorEvent",
949+
// Charge for validation/storing events.
950+
mem + validate_entries + validate_utf8,
951+
// Charge for forming the AMT and returning the events to the client.
952+
// one copy into the AMT, one copy to the client.
953+
hash + (mem * 2u32),
977954
)
978955
}
979956
}

0 commit comments

Comments
 (0)