1use std::{collections::HashSet, sync::Arc};
6
7use anyhow::Result;
8use iota_config::{
9 transaction_deny_config::TransactionDenyConfig, verifier_signing_config::VerifierSigningConfig,
10};
11use iota_execution::Executor;
12use iota_protocol_config::{Chain, ProtocolConfig, ProtocolVersion};
13use iota_sdk_types::{Transaction, TransactionEffects};
14use iota_transaction_checks::VerifierLimitsSource;
15use iota_types::{
16 committee::{Committee, EpochId},
17 effects::TransactionEffectsAPI,
18 error::IotaResult,
19 gas::IotaGasStatus,
20 gas_coin::mock_simulation_gas_coin,
21 inner_temporary_store::InnerTemporaryStore,
22 iota_system_state::{
23 IotaSystemState, IotaSystemStateTrait,
24 epoch_start_iota_system_state::{EpochStartSystemState, EpochStartSystemStateTrait},
25 },
26 metrics::{BytecodeVerifierMetrics, LimitsMetrics},
27 transaction::{ObjectReadResult, TransactionAPI, VerifiedTransaction},
28 transaction_executor::{SimulateTransactionResult, VmChecks},
29};
30
31use crate::SimulatorStore;
32
33pub struct EpochState {
34 epoch_start_state: EpochStartSystemState,
35 committee: Committee,
36 protocol_config: ProtocolConfig,
37 limits_metrics: Arc<LimitsMetrics>,
38 bytecode_verifier_metrics: Arc<BytecodeVerifierMetrics>,
39 executor: Arc<dyn Executor + Send + Sync>,
40 next_consensus_round: u64,
44}
45
46impl EpochState {
47 pub fn new(system_state: IotaSystemState) -> Self {
48 let epoch_start_state = system_state.into_epoch_start_state();
49 let protocol_config =
50 ProtocolConfig::get_for_version(epoch_start_state.protocol_version(), Chain::Unknown);
51 Self::new_impl(protocol_config, epoch_start_state)
52 }
53
54 pub fn new_with_config(protocol_config: ProtocolConfig, system_state: IotaSystemState) -> Self {
63 let epoch_start_state = system_state.into_epoch_start_state();
64 assert_eq!(
65 protocol_config.version,
66 epoch_start_state.protocol_version()
67 );
68 Self::new_impl(protocol_config, epoch_start_state)
69 }
70
71 fn new_impl(protocol_config: ProtocolConfig, epoch_start_state: EpochStartSystemState) -> Self {
72 let committee = epoch_start_state.get_iota_committee();
73 let registry = prometheus_filtered::Registry::new();
74 let limits_metrics = Arc::new(LimitsMetrics::new(®istry));
75 let bytecode_verifier_metrics = Arc::new(BytecodeVerifierMetrics::new(®istry));
76 let executor = iota_execution::executor(&protocol_config, true, None).unwrap();
77
78 Self {
79 epoch_start_state,
80 committee,
81 protocol_config,
82 limits_metrics,
83 bytecode_verifier_metrics,
84 executor,
85 next_consensus_round: 0,
86 }
87 }
88
89 pub fn epoch(&self) -> EpochId {
90 self.epoch_start_state.epoch()
91 }
92
93 pub fn reference_gas_price(&self) -> u64 {
94 self.epoch_start_state.reference_gas_price()
95 }
96
97 pub fn next_consensus_round(&mut self) -> u64 {
98 let round = self.next_consensus_round;
99 self.next_consensus_round += 1;
100 round
101 }
102
103 pub fn committee(&self) -> &Committee {
104 &self.committee
105 }
106
107 pub fn epoch_start_state(&self) -> EpochStartSystemState {
108 self.epoch_start_state.clone()
109 }
110
111 pub fn protocol_version(&self) -> ProtocolVersion {
112 self.protocol_config().version
113 }
114
115 pub fn protocol_config(&self) -> &ProtocolConfig {
116 &self.protocol_config
117 }
118
119 pub fn execute_transaction(
120 &self,
121 store: &dyn SimulatorStore,
122 deny_config: &TransactionDenyConfig,
123 verifier_signing_config: &VerifierSigningConfig,
124 transaction: &VerifiedTransaction,
125 ) -> Result<(
126 InnerTemporaryStore,
127 IotaGasStatus,
128 TransactionEffects,
129 Result<(), iota_types::error::ExecutionError>,
130 )> {
131 let tx_digest = *transaction.digest();
132 let tx = transaction.data().transaction();
133 let input_object_kinds = tx.input_objects()?;
134 let receiving_object_refs = tx.receiving_objects();
135
136 iota_transaction_checks::deny::check_transaction_for_validation(
137 tx,
138 transaction.signatures(),
139 &input_object_kinds,
140 &receiving_object_refs,
141 deny_config,
142 &store,
143 )?;
144
145 let (input_objects, receiving_objects) = store.read_objects_for_synchronous_execution(
146 &tx_digest,
147 &input_object_kinds,
148 &receiving_object_refs,
149 )?;
150
151 let authenticator_gas_budget = 0;
154
155 let (gas_status, checked_input_objects) = iota_transaction_checks::check_transaction_input(
158 &self.protocol_config,
159 self.epoch_start_state.reference_gas_price(),
160 transaction.data().transaction(),
161 input_objects,
162 &receiving_objects,
163 &self.bytecode_verifier_metrics,
164 VerifierLimitsSource::NodeConfig(verifier_signing_config),
165 authenticator_gas_budget,
166 )?;
167
168 let transaction = transaction.data().transaction();
169 let (kind, signer, gas_data) = transaction.execution_parts();
170 let (inner_temp_store, gas_status, effects, _timings, result) =
171 self.executor.execute_transaction_to_effects(
172 store.backing_store(),
173 &self.protocol_config,
174 self.limits_metrics.clone(),
175 false, &HashSet::new(), &self.epoch_start_state.epoch(),
178 self.epoch_start_state.epoch_start_timestamp_ms(),
179 checked_input_objects,
180 gas_data,
181 gas_status,
182 kind,
183 signer,
184 tx_digest,
185 &mut None,
186 );
187 Ok((inner_temp_store, gas_status, effects, result))
188 }
189
190 pub fn simulate_transaction(
197 &self,
198 store: &dyn SimulatorStore,
199 deny_config: &TransactionDenyConfig,
200 verifier_signing_config: &VerifierSigningConfig,
201 mut transaction: Transaction,
202 checks: VmChecks,
203 ) -> IotaResult<SimulateTransactionResult> {
204 transaction.check_serialized_size(&self.protocol_config)?;
205
206 transaction.validity_check_no_gas_check(&self.protocol_config)?;
208
209 transaction.check_gas_payment_size(&self.protocol_config)?;
213
214 let input_object_kinds = transaction.input_objects()?;
215 let receiving_object_refs = transaction.receiving_objects();
216
217 iota_transaction_checks::deny::check_transaction_for_validation(
219 &transaction,
220 &[],
221 &input_object_kinds,
222 &receiving_object_refs,
223 deny_config,
224 store,
225 )?;
226
227 let (mut input_objects, receiving_objects) = store.read_objects_for_synchronous_execution(
229 &transaction.digest(),
230 &input_object_kinds,
231 &receiving_object_refs,
232 )?;
233
234 let mock_gas_id = if transaction.gas().is_empty() {
236 let mock_gas_object = mock_simulation_gas_coin(transaction.gas_data().owner);
237 let mock_gas_object_ref = mock_gas_object.object_ref();
238 transaction.gas_data_mut().objects = vec![mock_gas_object_ref];
239 input_objects.push(ObjectReadResult::new_from_gas_object(&mock_gas_object));
240 Some(mock_gas_object.id())
241 } else {
242 None
243 };
244
245 iota_types::gas::fill_in_unset_simulation_gas(
246 &mut transaction,
247 &input_objects,
248 self.epoch_start_state.reference_gas_price(),
249 &self.protocol_config,
250 );
251
252 let authenticator_gas_budget = 0;
255
256 let (gas_status, checked_input_objects) = if checks.enabled() {
259 iota_transaction_checks::check_transaction_input(
260 &self.protocol_config,
261 self.epoch_start_state.reference_gas_price(),
262 &transaction,
263 input_objects,
264 &receiving_objects,
265 &self.bytecode_verifier_metrics,
266 VerifierLimitsSource::NodeConfig(verifier_signing_config),
267 authenticator_gas_budget,
268 )?
269 } else {
270 iota_types::gas::check_gas_coins_cover_budget_in_simulation(
276 &input_objects,
277 transaction.gas(),
278 transaction.gas_budget(),
279 )?;
280
281 let checked_input_objects = iota_transaction_checks::check_simulation_input(
282 &self.protocol_config,
283 transaction.kind(),
284 input_objects,
285 receiving_objects,
286 )?;
287 let gas_status = IotaGasStatus::new(
288 transaction.gas_budget(),
289 transaction.gas_price(),
290 self.epoch_start_state.reference_gas_price(),
291 &self.protocol_config,
292 )?;
293
294 (gas_status, checked_input_objects)
295 };
296
297 let (kind, signer, gas_data) = transaction.execution_parts();
299 let (inner_temp_store, _, effects, execution_result) =
300 self.executor.dev_inspect_transaction(
301 store.backing_store(),
302 &self.protocol_config,
303 self.limits_metrics.clone(),
304 false, &HashSet::new(), &self.epoch_start_state.epoch(),
307 self.epoch_start_state.epoch_start_timestamp_ms(),
308 checked_input_objects,
309 gas_data,
310 gas_status,
311 kind,
312 signer,
313 transaction.digest(),
314 checks.disabled(),
315 );
316
317 let mut input_objects = inner_temp_store.input_objects;
318 iota_types::storage::extend_input_objects_with_loaded_runtime_objects(
319 &mut input_objects,
320 &effects,
321 &inner_temp_store.loaded_runtime_objects,
322 store.backing_store().as_object_store(),
323 );
324
325 Ok(SimulateTransactionResult {
326 input_objects,
327 output_objects: inner_temp_store.written,
328 events: effects.events_digest().map(|_| inner_temp_store.events),
329 effects,
330 execution_result,
331 mock_gas_id,
332 suggested_gas_price: None,
333 gas_data: transaction.gas_data().clone(),
334 })
335 }
336}