iota_move_natives_latest/
event.rs1use std::collections::VecDeque;
6
7use iota_types::error::VMMemoryLimitExceededSubStatusCode;
8use move_binary_format::errors::{PartialVMError, PartialVMResult};
9use move_core_types::{gas_algebra::InternalGas, language_storage::TypeTag, vm_status::StatusCode};
10use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
11use move_vm_types::{
12 loaded_data::runtime_types::Type, natives::function::NativeResult, values::Value,
13};
14use smallvec::smallvec;
15
16use crate::{NativesCostTable, legacy_test_cost, object_runtime::ObjectRuntime};
17
18#[derive(Clone, Debug)]
19pub struct EventEmitCostParams {
20 pub event_emit_cost_base: InternalGas,
21 pub event_emit_value_size_derivation_cost_per_byte: InternalGas,
22 pub event_emit_tag_size_derivation_cost_per_byte: InternalGas,
23 pub event_emit_output_cost_per_byte: InternalGas,
24}
25pub fn emit(
40 context: &mut NativeContext,
41 mut ty_args: Vec<Type>,
42 mut args: VecDeque<Value>,
43) -> PartialVMResult<NativeResult> {
44 debug_assert!(ty_args.len() == 1);
45 debug_assert!(args.len() == 1);
46
47 let event_emit_cost_params = context
48 .extensions_mut()
49 .get::<NativesCostTable>()
50 .event_emit_cost_params
51 .clone();
52
53 native_charge_gas_early_exit!(context, event_emit_cost_params.event_emit_cost_base);
54
55 let ty = ty_args.pop().unwrap();
56 let event_value = args.pop_back().unwrap();
57
58 let event_value_size = event_value.legacy_size();
59
60 native_charge_gas_early_exit!(
62 context,
63 event_emit_cost_params.event_emit_value_size_derivation_cost_per_byte
64 * u64::from(event_value_size).into()
65 );
66
67 let tag = match context.type_to_type_tag(&ty)? {
68 TypeTag::Struct(s) => s,
69 _ => {
70 return Err(
71 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
72 .with_message("IOTA verifier guarantees this is a struct".to_string()),
73 );
74 }
75 };
76 let tag_size = tag.abstract_size_for_gas_metering();
77
78 native_charge_gas_early_exit!(
80 context,
81 event_emit_cost_params.event_emit_tag_size_derivation_cost_per_byte
82 * u64::from(tag_size).into()
83 );
84
85 let obj_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
86 let max_event_emit_size = obj_runtime.protocol_config.max_event_emit_size();
87 let ev_size = u64::from(tag_size + event_value_size);
88 if ev_size > max_event_emit_size {
90 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
91 .with_message(format!(
92 "Emitting event of size {ev_size} bytes. Limit is {max_event_emit_size} bytes."
93 ))
94 .with_sub_status(
95 VMMemoryLimitExceededSubStatusCode::EVENT_SIZE_LIMIT_EXCEEDED as u64,
96 ));
97 }
98
99 if let Some(max_event_emit_size_total) = obj_runtime
102 .protocol_config
103 .max_event_emit_size_total_as_option()
104 {
105 let total_events_size = obj_runtime.state.total_events_size() + ev_size;
106 if total_events_size > max_event_emit_size_total {
107 return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
108 .with_message(format!(
109 "Reached total event size of size {total_events_size} bytes. Limit is {max_event_emit_size_total} bytes."
110 ))
111 .with_sub_status(
112 VMMemoryLimitExceededSubStatusCode::TOTAL_EVENT_SIZE_LIMIT_EXCEEDED as u64,
113 ));
114 }
115 obj_runtime.state.incr_total_events_size(ev_size);
116 }
117 native_charge_gas_early_exit!(
119 context,
120 event_emit_cost_params.event_emit_output_cost_per_byte * ev_size.into()
121 );
122
123 let obj_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
124
125 obj_runtime.emit_event(ty, *tag, event_value)?;
126 Ok(NativeResult::ok(context.gas_used(), smallvec![]))
127}
128
129pub fn num_events(
131 context: &mut NativeContext,
132 ty_args: Vec<Type>,
133 args: VecDeque<Value>,
134) -> PartialVMResult<NativeResult> {
135 assert!(ty_args.is_empty());
136 assert!(args.is_empty());
137 let object_runtime_ref: &ObjectRuntime = context.extensions().get();
138 let num_events = object_runtime_ref.state.events().len();
139 Ok(NativeResult::ok(
140 legacy_test_cost(),
141 smallvec![Value::u32(num_events as u32)],
142 ))
143}
144
145pub fn get_events_by_type(
147 context: &mut NativeContext,
148 mut ty_args: Vec<Type>,
149 args: VecDeque<Value>,
150) -> PartialVMResult<NativeResult> {
151 assert_eq!(ty_args.len(), 1);
152 let specified_ty = ty_args.pop().unwrap();
153 assert!(args.is_empty());
154 let object_runtime_ref: &ObjectRuntime = context.extensions().get();
155 let matched_events = object_runtime_ref
156 .state
157 .events()
158 .iter()
159 .filter_map(|(ty, _, event)| {
160 if specified_ty == *ty {
161 Some(event.copy_value().unwrap())
162 } else {
163 None
164 }
165 })
166 .collect::<Vec<_>>();
167 Ok(NativeResult::ok(
168 legacy_test_cost(),
169 smallvec![Value::vector_for_testing_only(matched_events)],
170 ))
171}