@@ -7,7 +7,6 @@ use std::ops::Mul;
7
7
8
8
use anyhow:: Context ;
9
9
use fvm_shared:: crypto:: signature:: SignatureType ;
10
- use fvm_shared:: event:: { ActorEvent , Flags } ;
11
10
use fvm_shared:: piece:: PieceInfo ;
12
11
use fvm_shared:: sector:: {
13
12
AggregateSealVerifyProofAndInfos , RegisteredPoStProof , RegisteredSealProof , ReplicaUpdateInfo ,
@@ -27,6 +26,21 @@ use crate::kernel::SupportedHashes;
27
26
// https://docs.rs/wasmtime/2.0.2/wasmtime/struct.InstanceLimits.html#structfield.table_elements
28
27
const TABLE_ELEMENT_SIZE : u32 = 8 ;
29
28
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
+
30
44
/// Create a mapping from enum items to values in a way that guarantees at compile
31
45
/// time that we did not miss any member, in any of the prices, even if the enum
32
46
/// gets a new member later.
@@ -293,26 +307,15 @@ lazy_static! {
293
307
memory_fill_per_byte_cost: Gas :: from_milligas( 400 ) ,
294
308
} ,
295
309
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 {
307
313
flat: Gas :: new( 1750 ) ,
308
314
scale: Gas :: new( 25 ) ,
309
315
} ,
310
316
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 {
316
319
flat: Zero :: zero( ) ,
317
320
scale: Zero :: zero( ) ,
318
321
} ,
@@ -468,14 +471,14 @@ pub struct PriceList {
468
471
469
472
/// Gas cost to validate an ActorEvent as soon as it's received from the actor, and prior
470
473
/// 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 ,
475
475
476
476
/// Gas cost of doing lookups in the builtin actor mappings.
477
477
pub ( crate ) builtin_actor_manifest_lookup : Gas ,
478
478
479
+ /// Gas cost of utf8 parsing.
480
+ pub ( crate ) utf8_validation : ScalingCost ,
481
+
479
482
/// Gas cost of accessing the network context.
480
483
pub ( crate ) network_context : Gas ,
481
484
/// Gas cost of accessing the message context.
@@ -921,59 +924,33 @@ impl PriceList {
921
924
}
922
925
923
926
#[ 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.
949
929
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) ;
955
932
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 ) ;
960
939
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) ;
965
943
966
944
// 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 ) ;
968
946
969
947
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 ) ,
977
954
)
978
955
}
979
956
}
0 commit comments