iota_adapter_latest/
execution_engine.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5pub use checked::*;
6
7#[iota_macros::with_checked_arithmetic]
8mod checked {
9
10    use std::{collections::HashSet, sync::Arc};
11
12    use iota_move_natives::all_natives;
13    use iota_protocol_config::{LimitThresholdCrossed, ProtocolConfig, check_limit_by_meter};
14    #[cfg(msim)]
15    use iota_types::iota_system_state::advance_epoch_result_injection::maybe_modify_result;
16    use iota_types::{
17        IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS, IOTA_FRAMEWORK_PACKAGE_ID,
18        IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID,
19        authenticator_state::{
20            AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME,
21            AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME, AUTHENTICATOR_STATE_MODULE_NAME,
22            AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
23        },
24        balance::{
25            BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
26            BALANCE_MODULE_NAME,
27        },
28        base_types::{
29            IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, TxContext,
30        },
31        clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME},
32        committee::EpochId,
33        effects::TransactionEffects,
34        error::{ExecutionError, ExecutionErrorKind},
35        execution::{ExecutionResults, ExecutionResultsV1, is_certificate_denied},
36        execution_config_utils::to_binary_config,
37        execution_status::{CongestedObjects, ExecutionStatus},
38        gas::{GasCostSummary, IotaGasStatus},
39        gas_coin::GAS,
40        inner_temporary_store::InnerTemporaryStore,
41        iota_system_state::{
42            ADVANCE_EPOCH_FUNCTION_NAME, AdvanceEpochParams, IOTA_SYSTEM_MODULE_NAME,
43        },
44        messages_checkpoint::CheckpointTimestamp,
45        metrics::LimitsMetrics,
46        object::{OBJECT_START_VERSION, Object, ObjectInner},
47        programmable_transaction_builder::ProgrammableTransactionBuilder,
48        randomness_state::{RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_UPDATE_FUNCTION_NAME},
49        storage::{BackingStore, Storage},
50        transaction::{
51            Argument, AuthenticatorStateExpire, AuthenticatorStateUpdateV1, CallArg, ChangeEpoch,
52            ChangeEpochV2, ChangeEpochV3, CheckedInputObjects, Command, EndOfEpochTransactionKind,
53            GenesisTransaction, ObjectArg, ProgrammableTransaction, RandomnessStateUpdate,
54            TransactionKind,
55        },
56    };
57    use move_binary_format::CompiledModule;
58    use move_trace_format::format::MoveTraceBuilder;
59    use move_vm_runtime::move_vm::MoveVM;
60    use tracing::{info, instrument, trace, warn};
61
62    use crate::{
63        adapter::new_move_vm,
64        execution_mode::{self, ExecutionMode},
65        gas_charger::GasCharger,
66        programmable_transactions,
67        temporary_store::TemporaryStore,
68        type_layout_resolver::TypeLayoutResolver,
69    };
70
71    /// The main entry point to the adapter's transaction execution. It
72    /// prepares a transaction for execution, then executes it through an
73    /// inner execution method and finally produces an instance of
74    /// transaction effects. It also returns the inner temporary store, which
75    /// contains the objects resulting from the transaction execution, the gas
76    /// status instance, which tracks the gas usage, and the execution result.
77    /// The function handles transaction execution based on the provided
78    /// `TransactionKind`. It checks for any expensive operations, manages
79    /// shared object references, and ensures transaction dependencies are
80    /// met. The returned objects are not committed to the store until the
81    /// resulting effects are applied by the caller.
82    #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
83    pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
84        store: &dyn BackingStore,
85        input_objects: CheckedInputObjects,
86        gas_coins: Vec<ObjectRef>,
87        gas_status: IotaGasStatus,
88        transaction_kind: TransactionKind,
89        transaction_signer: IotaAddress,
90        transaction_digest: TransactionDigest,
91        move_vm: &Arc<MoveVM>,
92        epoch_id: &EpochId,
93        epoch_timestamp_ms: u64,
94        protocol_config: &ProtocolConfig,
95        metrics: Arc<LimitsMetrics>,
96        enable_expensive_checks: bool,
97        certificate_deny_set: &HashSet<TransactionDigest>,
98        trace_builder_opt: &mut Option<MoveTraceBuilder>,
99    ) -> (
100        InnerTemporaryStore,
101        IotaGasStatus,
102        TransactionEffects,
103        Result<Mode::ExecutionResults, ExecutionError>,
104    ) {
105        let input_objects = input_objects.into_inner();
106        let mutable_inputs = if enable_expensive_checks {
107            input_objects.mutable_inputs().keys().copied().collect()
108        } else {
109            HashSet::new()
110        };
111        let shared_object_refs = input_objects.filter_shared_objects();
112        let receiving_objects = transaction_kind.receiving_objects();
113        let mut transaction_dependencies = input_objects.transaction_dependencies();
114        let contains_deleted_input = input_objects.contains_deleted_objects();
115        let cancelled_objects = input_objects.get_cancelled_objects();
116
117        let mut temporary_store = TemporaryStore::new(
118            store,
119            input_objects,
120            receiving_objects,
121            transaction_digest,
122            protocol_config,
123            *epoch_id,
124        );
125
126        let mut gas_charger =
127            GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
128
129        let mut tx_ctx = TxContext::new_from_components(
130            &transaction_signer,
131            &transaction_digest,
132            epoch_id,
133            epoch_timestamp_ms,
134        );
135
136        let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
137
138        let deny_cert = is_certificate_denied(&transaction_digest, certificate_deny_set);
139        let (gas_cost_summary, execution_result) = execute_transaction::<Mode>(
140            &mut temporary_store,
141            transaction_kind,
142            &mut gas_charger,
143            &mut tx_ctx,
144            move_vm,
145            protocol_config,
146            metrics,
147            enable_expensive_checks,
148            deny_cert,
149            contains_deleted_input,
150            cancelled_objects,
151            trace_builder_opt,
152        );
153
154        let status = if let Err(error) = &execution_result {
155            // Elaborate errors in logs if they are unexpected or their status is terse.
156            use ExecutionErrorKind as K;
157            match error.kind() {
158                K::InvariantViolation | K::VMInvariantViolation => {
159                    #[skip_checked_arithmetic]
160                    tracing::error!(
161                        kind = ?error.kind(),
162                        tx_digest = ?transaction_digest,
163                        "INVARIANT VIOLATION! Source: {:?}",
164                        error.source(),
165                    );
166                }
167
168                K::IotaMoveVerificationError | K::VMVerificationOrDeserializationError => {
169                    #[skip_checked_arithmetic]
170                    tracing::debug!(
171                        kind = ?error.kind(),
172                        tx_digest = ?transaction_digest,
173                        "Verification Error. Source: {:?}",
174                        error.source(),
175                    );
176                }
177
178                K::PublishUpgradeMissingDependency | K::PublishUpgradeDependencyDowngrade => {
179                    #[skip_checked_arithmetic]
180                    tracing::debug!(
181                        kind = ?error.kind(),
182                        tx_digest = ?transaction_digest,
183                        "Publish/Upgrade Error. Source: {:?}",
184                        error.source(),
185                    )
186                }
187
188                _ => (),
189            };
190
191            let (status, command) = error.to_execution_status();
192            ExecutionStatus::new_failure(status, command)
193        } else {
194            ExecutionStatus::Success
195        };
196
197        #[skip_checked_arithmetic]
198        trace!(
199            tx_digest = ?transaction_digest,
200            computation_gas_cost = gas_cost_summary.computation_cost,
201            computation_gas_cost_burned = gas_cost_summary.computation_cost_burned,
202            storage_gas_cost = gas_cost_summary.storage_cost,
203            storage_gas_rebate = gas_cost_summary.storage_rebate,
204            "Finished execution of transaction with status {:?}",
205            status
206        );
207
208        // Genesis writes a special digest to indicate that an object was created during
209        // genesis and not written by any normal transaction - remove that from the
210        // dependencies
211        transaction_dependencies.remove(&TransactionDigest::genesis_marker());
212
213        if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
214            temporary_store
215                .check_ownership_invariants(
216                    &transaction_signer,
217                    &mut gas_charger,
218                    &mutable_inputs,
219                    is_epoch_change,
220                )
221                .unwrap()
222        } // else, in dev inspect mode and anything goes--don't check
223
224        let (inner, effects) = temporary_store.into_effects(
225            shared_object_refs,
226            &transaction_digest,
227            transaction_dependencies,
228            gas_cost_summary,
229            status,
230            &mut gas_charger,
231            *epoch_id,
232        );
233
234        (
235            inner,
236            gas_charger.into_gas_status(),
237            effects,
238            execution_result,
239        )
240    }
241
242    /// Function dedicated to the execution of a GenesisTransaction.
243    /// The function creates an `InnerTemporaryStore`, processes the input
244    /// objects, and executes the transaction in unmetered mode using the
245    /// `Genesis` execution mode. It returns an inner temporary store that
246    /// contains the objects found into the input `GenesisTransaction` by
247    /// adding the data for `previous_transaction` and `storage_rebate` fields.
248    pub fn execute_genesis_state_update(
249        store: &dyn BackingStore,
250        protocol_config: &ProtocolConfig,
251        metrics: Arc<LimitsMetrics>,
252        move_vm: &Arc<MoveVM>,
253        tx_context: &mut TxContext,
254        input_objects: CheckedInputObjects,
255        pt: ProgrammableTransaction,
256    ) -> Result<InnerTemporaryStore, ExecutionError> {
257        let input_objects = input_objects.into_inner();
258        let mut temporary_store = TemporaryStore::new(
259            store,
260            input_objects,
261            vec![],
262            tx_context.digest(),
263            protocol_config,
264            0,
265        );
266        let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
267        programmable_transactions::execution::execute::<execution_mode::Genesis>(
268            protocol_config,
269            metrics,
270            move_vm,
271            &mut temporary_store,
272            tx_context,
273            &mut gas_charger,
274            pt,
275            &mut None,
276        )?;
277        temporary_store.update_object_version_and_prev_tx();
278        Ok(temporary_store.into_inner())
279    }
280
281    /// Executes a transaction by processing the specified `TransactionKind`,
282    /// applying the necessary gas charges and running the main execution logic.
283    /// The function handles certain error conditions such as denied
284    /// certificate, deleted input objects, exceeded execution meter limits,
285    /// failed conservation checks. It also accounts for unmetered storage
286    /// rebates and adjusts for special cases like epoch change
287    /// transactions. Gas costs are managed through the `GasCharger`
288    /// argument; gas is also charged in case of errors.
289    #[instrument(name = "tx_execute", level = "debug", skip_all)]
290    fn execute_transaction<Mode: ExecutionMode>(
291        temporary_store: &mut TemporaryStore<'_>,
292        transaction_kind: TransactionKind,
293        gas_charger: &mut GasCharger,
294        tx_ctx: &mut TxContext,
295        move_vm: &Arc<MoveVM>,
296        protocol_config: &ProtocolConfig,
297        metrics: Arc<LimitsMetrics>,
298        enable_expensive_checks: bool,
299        deny_cert: bool,
300        contains_deleted_input: bool,
301        cancelled_objects: Option<(Vec<ObjectID>, SequenceNumber)>,
302        trace_builder_opt: &mut Option<MoveTraceBuilder>,
303    ) -> (
304        GasCostSummary,
305        Result<Mode::ExecutionResults, ExecutionError>,
306    ) {
307        gas_charger.smash_gas(temporary_store);
308
309        // At this point no charges have been applied yet
310        debug_assert!(
311            gas_charger.no_charges(),
312            "No gas charges must be applied yet"
313        );
314
315        let is_genesis_or_epoch_change_tx = matches!(transaction_kind, TransactionKind::Genesis(_))
316            || transaction_kind.is_end_of_epoch_tx();
317
318        let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
319
320        // We must charge object read here during transaction execution, because if this
321        // fails we must still ensure an effect is committed and all objects
322        // versions incremented
323        let result = gas_charger.charge_input_objects(temporary_store);
324        let mut result = result.and_then(|()| {
325            let mut execution_result = if deny_cert {
326                Err(ExecutionError::new(
327                    ExecutionErrorKind::CertificateDenied,
328                    None,
329                ))
330            } else if contains_deleted_input {
331                Err(ExecutionError::new(
332                    ExecutionErrorKind::InputObjectDeleted,
333                    None,
334                ))
335            } else if let Some((cancelled_objects, reason)) = cancelled_objects {
336                match reason {
337                    version if version.is_congested() => Err(ExecutionError::new(
338                        if protocol_config.congestion_control_gas_price_feedback_mechanism() {
339                            ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestionV2 {
340                                congested_objects: CongestedObjects(cancelled_objects),
341                                suggested_gas_price: version
342                                    .get_congested_version_suggested_gas_price(),
343                            }
344                        } else {
345                            // WARN: do not remove this `else` branch even after
346                            // `congestion_control_gas_price_feedback_mechanism` is enabled
347                            // on the mainnet. It must be kept to be able to replay old
348                            // transaction data.
349                            ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
350                                congested_objects: CongestedObjects(cancelled_objects),
351                            }
352                        },
353                        None,
354                    )),
355                    SequenceNumber::RANDOMNESS_UNAVAILABLE => Err(ExecutionError::new(
356                        ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable,
357                        None,
358                    )),
359                    _ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
360                }
361            } else {
362                execution_loop::<Mode>(
363                    temporary_store,
364                    transaction_kind,
365                    tx_ctx,
366                    move_vm,
367                    gas_charger,
368                    protocol_config,
369                    metrics.clone(),
370                    trace_builder_opt,
371                )
372            };
373
374            let meter_check = check_meter_limit(
375                temporary_store,
376                gas_charger,
377                protocol_config,
378                metrics.clone(),
379            );
380            if let Err(e) = meter_check {
381                execution_result = Err(e);
382            }
383
384            if execution_result.is_ok() {
385                let gas_check = check_written_objects_limit::<Mode>(
386                    temporary_store,
387                    gas_charger,
388                    protocol_config,
389                    metrics,
390                );
391                if let Err(e) = gas_check {
392                    execution_result = Err(e);
393                }
394            }
395
396            execution_result
397        });
398
399        let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
400        // For advance epoch transaction, we need to provide epoch rewards and rebates
401        // as extra information provided to check_iota_conserved, because we
402        // mint rewards, and burn the rebates. We also need to pass in the
403        // unmetered_storage_rebate because storage rebate is not reflected in
404        // the storage_rebate of gas summary. This is a bit confusing.
405        // We could probably clean up the code a bit.
406        // Put all the storage rebate accumulated in the system transaction
407        // to the 0x5 object so that it's not lost.
408        temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
409
410        if let Err(e) = run_conservation_checks::<Mode>(
411            temporary_store,
412            gas_charger,
413            tx_ctx,
414            move_vm,
415            enable_expensive_checks,
416            &cost_summary,
417            is_genesis_or_epoch_change_tx,
418            advance_epoch_gas_summary,
419        ) {
420            // FIXME: we cannot fail the transaction if this is an epoch change transaction.
421            result = Err(e);
422        }
423
424        (cost_summary, result)
425    }
426
427    /// Performs IOTA conservation checks during transaction execution, ensuring
428    /// that the transaction does not create or destroy IOTA. If
429    /// conservation is violated, the function attempts to recover
430    /// by resetting the gas charger, recharging gas, and rechecking
431    /// conservation. If recovery fails, it panics to avoid IOTA creation or
432    /// destruction. These checks include both simple and expensive
433    /// checks based on the configuration and are skipped for genesis or epoch
434    /// change transactions.
435    #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
436    fn run_conservation_checks<Mode: ExecutionMode>(
437        temporary_store: &mut TemporaryStore<'_>,
438        gas_charger: &mut GasCharger,
439        tx_ctx: &mut TxContext,
440        move_vm: &Arc<MoveVM>,
441        enable_expensive_checks: bool,
442        cost_summary: &GasCostSummary,
443        is_genesis_or_epoch_change_tx: bool,
444        advance_epoch_gas_summary: Option<(u64, u64)>,
445    ) -> Result<(), ExecutionError> {
446        let mut result: std::result::Result<(), iota_types::error::ExecutionError> = Ok(());
447        if !is_genesis_or_epoch_change_tx && !Mode::skip_conservation_checks() {
448            // ensure that this transaction did not create or destroy IOTA, try to recover
449            // if the check fails
450            let conservation_result = {
451                temporary_store
452                    .check_iota_conserved(cost_summary)
453                    .and_then(|()| {
454                        if enable_expensive_checks {
455                            // ensure that this transaction did not create or destroy IOTA, try to
456                            // recover if the check fails
457                            let mut layout_resolver =
458                                TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
459                            temporary_store.check_iota_conserved_expensive(
460                                cost_summary,
461                                advance_epoch_gas_summary,
462                                &mut layout_resolver,
463                            )
464                        } else {
465                            Ok(())
466                        }
467                    })
468            };
469            if let Err(conservation_err) = conservation_result {
470                // conservation violated. try to avoid panic by dumping all writes, charging for
471                // gas, re-checking conservation, and surfacing an aborted
472                // transaction with an invariant violation if all of that works
473                result = Err(conservation_err);
474                gas_charger.reset(temporary_store);
475                gas_charger.charge_gas(temporary_store, &mut result);
476                // check conservation once more
477                if let Err(recovery_err) = {
478                    temporary_store
479                        .check_iota_conserved(cost_summary)
480                        .and_then(|()| {
481                            if enable_expensive_checks {
482                                // ensure that this transaction did not create or destroy IOTA, try
483                                // to recover if the check fails
484                                let mut layout_resolver =
485                                    TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
486                                temporary_store.check_iota_conserved_expensive(
487                                    cost_summary,
488                                    advance_epoch_gas_summary,
489                                    &mut layout_resolver,
490                                )
491                            } else {
492                                Ok(())
493                            }
494                        })
495                } {
496                    // if we still fail, it's a problem with gas
497                    // charging that happens even in the "aborted" case--no other option but panic.
498                    // we will create or destroy IOTA otherwise
499                    panic!(
500                        "IOTA conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
501                        tx_ctx.digest(),
502                        recovery_err,
503                        gas_charger.summary()
504                    )
505                }
506            }
507        } // else, we're in the genesis transaction which mints the IOTA supply, and hence
508        // does not satisfy IOTA conservation, or we're in the non-production
509        // dev inspect mode which allows us to violate conservation
510        result
511    }
512
513    /// Checks if the estimated size of transaction effects exceeds predefined
514    /// limits based on the protocol configuration. For metered
515    /// transactions, it enforces hard limits, while for system transactions, it
516    /// allows soft limits with warnings.
517    #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
518    fn check_meter_limit(
519        temporary_store: &mut TemporaryStore<'_>,
520        gas_charger: &mut GasCharger,
521        protocol_config: &ProtocolConfig,
522        metrics: Arc<LimitsMetrics>,
523    ) -> Result<(), ExecutionError> {
524        let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
525
526        // Check if a limit threshold was crossed.
527        // For metered transactions, there is not soft limit.
528        // For system transactions, we allow a soft limit with alerting, and a hard
529        // limit where we terminate
530        match check_limit_by_meter!(
531            !gas_charger.is_unmetered(),
532            effects_estimated_size,
533            protocol_config.max_serialized_tx_effects_size_bytes(),
534            protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
535            metrics.excessive_estimated_effects_size
536        ) {
537            LimitThresholdCrossed::None => Ok(()),
538            LimitThresholdCrossed::Soft(_, limit) => {
539                warn!(
540                    effects_estimated_size = effects_estimated_size,
541                    soft_limit = limit,
542                    "Estimated transaction effects size crossed soft limit",
543                );
544                Ok(())
545            }
546            LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
547                ExecutionErrorKind::EffectsTooLarge {
548                    current_size: effects_estimated_size as u64,
549                    max_size: lim as u64,
550                },
551                "Transaction effects are too large",
552            )),
553        }
554    }
555
556    /// Checks if the total size of written objects in the transaction exceeds
557    /// the limits defined in the protocol configuration. For metered
558    /// transactions, it enforces a hard limit, while for system transactions,
559    /// it allows a soft limit with warnings.
560    #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
561    fn check_written_objects_limit<Mode: ExecutionMode>(
562        temporary_store: &mut TemporaryStore<'_>,
563        gas_charger: &mut GasCharger,
564        protocol_config: &ProtocolConfig,
565        metrics: Arc<LimitsMetrics>,
566    ) -> Result<(), ExecutionError> {
567        if let (Some(normal_lim), Some(system_lim)) = (
568            protocol_config.max_size_written_objects_as_option(),
569            protocol_config.max_size_written_objects_system_tx_as_option(),
570        ) {
571            let written_objects_size = temporary_store.written_objects_size();
572
573            match check_limit_by_meter!(
574                !gas_charger.is_unmetered(),
575                written_objects_size,
576                normal_lim,
577                system_lim,
578                metrics.excessive_written_objects_size
579            ) {
580                LimitThresholdCrossed::None => (),
581                LimitThresholdCrossed::Soft(_, limit) => {
582                    warn!(
583                        written_objects_size = written_objects_size,
584                        soft_limit = limit,
585                        "Written objects size crossed soft limit",
586                    )
587                }
588                LimitThresholdCrossed::Hard(_, lim) => {
589                    return Err(ExecutionError::new_with_source(
590                        ExecutionErrorKind::WrittenObjectsTooLarge {
591                            current_size: written_objects_size as u64,
592                            max_size: lim as u64,
593                        },
594                        "Written objects size crossed hard limit",
595                    ));
596                }
597            };
598        }
599
600        Ok(())
601    }
602
603    /// Executes the given transaction based on its `TransactionKind` by
604    /// processing it through corresponding handlers such as epoch changes,
605    /// genesis transactions, consensus commit prologues, and programmable
606    /// transactions. For each type of transaction, the corresponding logic is
607    /// invoked, such as advancing the epoch, setting up consensus commits, or
608    /// executing a programmable transaction.
609    #[instrument(level = "debug", skip_all)]
610    fn execution_loop<Mode: ExecutionMode>(
611        temporary_store: &mut TemporaryStore<'_>,
612        transaction_kind: TransactionKind,
613        tx_ctx: &mut TxContext,
614        move_vm: &Arc<MoveVM>,
615        gas_charger: &mut GasCharger,
616        protocol_config: &ProtocolConfig,
617        metrics: Arc<LimitsMetrics>,
618        trace_builder_opt: &mut Option<MoveTraceBuilder>,
619    ) -> Result<Mode::ExecutionResults, ExecutionError> {
620        let result = match transaction_kind {
621            TransactionKind::Genesis(GenesisTransaction { objects, events }) => {
622                if tx_ctx.epoch() != 0 {
623                    panic!("BUG: Genesis Transactions can only be executed in epoch 0");
624                }
625
626                for genesis_object in objects {
627                    match genesis_object {
628                        iota_types::transaction::GenesisObject::RawObject { data, owner } => {
629                            let object = ObjectInner {
630                                data,
631                                owner,
632                                previous_transaction: tx_ctx.digest(),
633                                storage_rebate: 0,
634                            };
635                            temporary_store.create_object(object.into());
636                        }
637                    }
638                }
639
640                temporary_store.record_execution_results(ExecutionResults::V1(
641                    ExecutionResultsV1 {
642                        user_events: events,
643                        ..Default::default()
644                    },
645                ));
646
647                Ok(Mode::empty_results())
648            }
649            TransactionKind::ConsensusCommitPrologueV1(prologue) => {
650                setup_consensus_commit(
651                    prologue.commit_timestamp_ms,
652                    temporary_store,
653                    tx_ctx,
654                    move_vm,
655                    gas_charger,
656                    protocol_config,
657                    metrics,
658                    trace_builder_opt,
659                )
660                .expect("ConsensusCommitPrologueV1 cannot fail");
661                Ok(Mode::empty_results())
662            }
663            TransactionKind::ProgrammableTransaction(pt) => {
664                programmable_transactions::execution::execute::<Mode>(
665                    protocol_config,
666                    metrics,
667                    move_vm,
668                    temporary_store,
669                    tx_ctx,
670                    gas_charger,
671                    pt,
672                    trace_builder_opt,
673                )
674            }
675            TransactionKind::EndOfEpochTransaction(txns) => {
676                let mut builder = ProgrammableTransactionBuilder::new();
677                let len = txns.len();
678                for (i, tx) in txns.into_iter().enumerate() {
679                    match tx {
680                        EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
681                            assert_eq!(i, len - 1);
682                            advance_epoch_v1(
683                                builder,
684                                change_epoch,
685                                temporary_store,
686                                tx_ctx,
687                                move_vm,
688                                gas_charger,
689                                protocol_config,
690                                metrics,
691                                trace_builder_opt,
692                            )?;
693                            return Ok(Mode::empty_results());
694                        }
695                        EndOfEpochTransactionKind::ChangeEpochV2(change_epoch_v2) => {
696                            assert_eq!(i, len - 1);
697                            advance_epoch_v2(
698                                builder,
699                                change_epoch_v2,
700                                temporary_store,
701                                tx_ctx,
702                                move_vm,
703                                gas_charger,
704                                protocol_config,
705                                metrics,
706                                trace_builder_opt,
707                            )?;
708                            return Ok(Mode::empty_results());
709                        }
710                        EndOfEpochTransactionKind::ChangeEpochV3(change_epoch_v3) => {
711                            assert_eq!(i, len - 1);
712                            advance_epoch_v3(
713                                builder,
714                                change_epoch_v3,
715                                temporary_store,
716                                tx_ctx,
717                                move_vm,
718                                gas_charger,
719                                protocol_config,
720                                metrics,
721                                trace_builder_opt,
722                            )?;
723                            return Ok(Mode::empty_results());
724                        }
725                        EndOfEpochTransactionKind::AuthenticatorStateCreate => {
726                            assert!(protocol_config.enable_jwk_consensus_updates());
727                            builder = setup_authenticator_state_create(builder);
728                        }
729                        EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
730                            assert!(protocol_config.enable_jwk_consensus_updates());
731
732                            // TODO: it would be nice if a failure of this function didn't cause
733                            // safe mode.
734                            builder = setup_authenticator_state_expire(builder, expire);
735                        }
736                    }
737                }
738                unreachable!(
739                    "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
740                )
741            }
742            TransactionKind::AuthenticatorStateUpdateV1(auth_state_update) => {
743                setup_authenticator_state_update(
744                    auth_state_update,
745                    temporary_store,
746                    tx_ctx,
747                    move_vm,
748                    gas_charger,
749                    protocol_config,
750                    metrics,
751                    trace_builder_opt,
752                )?;
753                Ok(Mode::empty_results())
754            }
755            TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
756                setup_randomness_state_update(
757                    randomness_state_update,
758                    temporary_store,
759                    tx_ctx,
760                    move_vm,
761                    gas_charger,
762                    protocol_config,
763                    metrics,
764                    trace_builder_opt,
765                )?;
766                Ok(Mode::empty_results())
767            }
768        }?;
769        temporary_store.check_execution_results_consistency()?;
770        Ok(result)
771    }
772
773    /// Mints epoch rewards by creating both storage and computation charges
774    /// using a `ProgrammableTransactionBuilder`. The function takes in the
775    /// `AdvanceEpochParams`, serializes the storage and computation
776    /// charges, and invokes the reward creation function within the IOTA
777    /// Prepares invocations for creating both storage and computation charges
778    /// with a `ProgrammableTransactionBuilder` using the `AdvanceEpochParams`.
779    /// The corresponding functions from the IOTA framework can be invoked later
780    /// during execution of the programmable transaction.
781    fn mint_epoch_rewards_in_pt(
782        builder: &mut ProgrammableTransactionBuilder,
783        params: &AdvanceEpochParams,
784    ) -> (Argument, Argument) {
785        // Create storage charges.
786        let storage_charge_arg = builder
787            .input(CallArg::Pure(
788                bcs::to_bytes(&params.storage_charge).unwrap(),
789            ))
790            .unwrap();
791        let storage_charges = builder.programmable_move_call(
792            IOTA_FRAMEWORK_PACKAGE_ID,
793            BALANCE_MODULE_NAME.to_owned(),
794            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
795            vec![GAS::type_tag()],
796            vec![storage_charge_arg],
797        );
798
799        // Create computation charges.
800        let computation_charge_arg = builder
801            .input(CallArg::Pure(
802                bcs::to_bytes(&params.computation_charge).unwrap(),
803            ))
804            .unwrap();
805        let computation_charges = builder.programmable_move_call(
806            IOTA_FRAMEWORK_PACKAGE_ID,
807            BALANCE_MODULE_NAME.to_owned(),
808            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
809            vec![GAS::type_tag()],
810            vec![computation_charge_arg],
811        );
812        (storage_charges, computation_charges)
813    }
814
815    /// Constructs a `ProgrammableTransaction` to advance the epoch. It creates
816    /// storage charges and computation charges by invoking
817    /// `mint_epoch_rewards_in_pt`, advances the epoch by setting up the
818    /// necessary arguments, such as epoch number, protocol version, storage
819    /// rebate, and slashing rate, and executing the `advance_epoch` function
820    /// within the IOTA system. Then, it destroys the storage rebates to
821    /// complete the transaction.
822    pub fn construct_advance_epoch_pt_impl(
823        mut builder: ProgrammableTransactionBuilder,
824        params: &AdvanceEpochParams,
825        call_arg_vec: Vec<CallArg>,
826    ) -> Result<ProgrammableTransaction, ExecutionError> {
827        // Create storage and computation charges and add them as arguments.
828        let (storage_charges, computation_charges) = mint_epoch_rewards_in_pt(&mut builder, params);
829        let mut arguments = vec![
830            builder
831                .pure(params.validator_subsidy)
832                .expect("bcs encoding a u64 should not fail"),
833            storage_charges,
834            computation_charges,
835        ];
836
837        let call_arg_arguments = call_arg_vec
838            .into_iter()
839            .map(|a| builder.input(a))
840            .collect::<Result<_, _>>();
841
842        assert_invariant!(
843            call_arg_arguments.is_ok(),
844            "Unable to generate args for advance_epoch transaction!"
845        );
846
847        arguments.append(&mut call_arg_arguments.unwrap());
848
849        info!("Call arguments to advance_epoch transaction: {:?}", params);
850
851        let storage_rebates = builder.programmable_move_call(
852            IOTA_SYSTEM_PACKAGE_ID,
853            IOTA_SYSTEM_MODULE_NAME.to_owned(),
854            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
855            vec![],
856            arguments,
857        );
858
859        // Step 3: Destroy the storage rebates.
860        builder.programmable_move_call(
861            IOTA_FRAMEWORK_PACKAGE_ID,
862            BALANCE_MODULE_NAME.to_owned(),
863            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
864            vec![GAS::type_tag()],
865            vec![storage_rebates],
866        );
867        Ok(builder.finish())
868    }
869
870    pub fn construct_advance_epoch_pt_v1(
871        builder: ProgrammableTransactionBuilder,
872        params: &AdvanceEpochParams,
873    ) -> Result<ProgrammableTransaction, ExecutionError> {
874        // the first three arguments to the advance_epoch function, namely
875        // validator_subsidy, storage_charges and computation_charges, are
876        // common to both v1 and v2 and are added in `construct_advance_epoch_pt_impl`.
877        // The remaining arguments are added here.
878        let call_arg_vec = vec![
879            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
880            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
881            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
882            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
883            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
884            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
885            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
886        ];
887        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
888    }
889
890    pub fn construct_advance_epoch_pt_v2(
891        builder: ProgrammableTransactionBuilder,
892        params: &AdvanceEpochParams,
893    ) -> Result<ProgrammableTransaction, ExecutionError> {
894        // the first three arguments to the advance_epoch function, namely
895        // validator_subsidy, storage_charges and computation_charges, are
896        // common to both v1 and v2 and are added in `construct_advance_epoch_pt_impl`.
897        // The remaining arguments are added here.
898        let call_arg_vec = vec![
899            CallArg::Pure(bcs::to_bytes(&params.computation_charge_burned).unwrap()), /* computation_charge_burned: u64 */
900            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
901            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
902            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
903            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
904            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
905            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
906            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
907            CallArg::Pure(bcs::to_bytes(&params.max_committee_members_count).unwrap()), /* max_committee_members_count: u64 */
908        ];
909        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
910    }
911
912    pub fn construct_advance_epoch_pt_v3(
913        builder: ProgrammableTransactionBuilder,
914        params: &AdvanceEpochParams,
915    ) -> Result<ProgrammableTransaction, ExecutionError> {
916        // the first three arguments to the advance_epoch function, namely
917        // validator_subsidy, storage_charges and computation_charges, are
918        // common to both v1, v2 and v3 and are added in
919        // `construct_advance_epoch_pt_impl`. The remaining arguments are added
920        // here.
921        let call_arg_vec = vec![
922            CallArg::Pure(bcs::to_bytes(&params.computation_charge_burned).unwrap()), /* computation_charge_burned: u64 */
923            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
924            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
925            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
926            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
927            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
928            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
929            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
930            CallArg::Pure(bcs::to_bytes(&params.max_committee_members_count).unwrap()), /* max_committee_members_count: u64 */
931            CallArg::Pure(bcs::to_bytes(&params.eligible_active_validators).unwrap()), /* eligible_active_validators: Vec<u64> */
932        ];
933        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
934    }
935
936    /// Advances the epoch by executing a `ProgrammableTransaction`. If the
937    /// transaction fails, it switches to safe mode and retries the epoch
938    /// advancement in a more controlled environment. The function also
939    /// handles the publication and upgrade of system packages for the new
940    /// epoch. If any system package is added or upgraded, it ensures the
941    /// proper execution and storage of the changes.
942    fn advance_epoch_impl(
943        advance_epoch_pt: ProgrammableTransaction,
944        params: AdvanceEpochParams,
945        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
946        temporary_store: &mut TemporaryStore<'_>,
947        tx_ctx: &mut TxContext,
948        move_vm: &Arc<MoveVM>,
949        gas_charger: &mut GasCharger,
950        protocol_config: &ProtocolConfig,
951        metrics: Arc<LimitsMetrics>,
952        trace_builder_opt: &mut Option<MoveTraceBuilder>,
953    ) -> Result<(), ExecutionError> {
954        let result = programmable_transactions::execution::execute::<execution_mode::System>(
955            protocol_config,
956            metrics.clone(),
957            move_vm,
958            temporary_store,
959            tx_ctx,
960            gas_charger,
961            advance_epoch_pt,
962            trace_builder_opt,
963        );
964
965        #[cfg(msim)]
966        let result = maybe_modify_result(result, params.epoch);
967
968        if result.is_err() {
969            tracing::error!(
970                "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx params: {:?}",
971                result.as_ref().err(),
972                temporary_store.objects(),
973                params,
974            );
975            temporary_store.drop_writes();
976            // Must reset the storage rebate since we are re-executing.
977            gas_charger.reset_storage_cost_and_rebate();
978
979            temporary_store.advance_epoch_safe_mode(&params, protocol_config);
980        }
981
982        let new_vm = new_move_vm(
983            all_natives(/* silent */ true, protocol_config),
984            protocol_config,
985            // enable_profiler
986            None,
987        )
988        .expect("Failed to create new MoveVM");
989        process_system_packages(
990            system_packages,
991            temporary_store,
992            tx_ctx,
993            &new_vm,
994            gas_charger,
995            protocol_config,
996            metrics,
997            trace_builder_opt,
998        );
999
1000        Ok(())
1001    }
1002
1003    /// Advances the epoch for the given `ChangeEpoch` transaction kind by
1004    /// constructing a programmable transaction, executing it and processing the
1005    /// system packages.
1006    fn advance_epoch_v1(
1007        builder: ProgrammableTransactionBuilder,
1008        change_epoch: ChangeEpoch,
1009        temporary_store: &mut TemporaryStore<'_>,
1010        tx_ctx: &mut TxContext,
1011        move_vm: &Arc<MoveVM>,
1012        gas_charger: &mut GasCharger,
1013        protocol_config: &ProtocolConfig,
1014        metrics: Arc<LimitsMetrics>,
1015        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1016    ) -> Result<(), ExecutionError> {
1017        let params = AdvanceEpochParams {
1018            epoch: change_epoch.epoch,
1019            next_protocol_version: change_epoch.protocol_version,
1020            validator_subsidy: protocol_config.validator_target_reward(),
1021            storage_charge: change_epoch.storage_charge,
1022            computation_charge: change_epoch.computation_charge,
1023            // all computation charge is burned in v1
1024            computation_charge_burned: change_epoch.computation_charge,
1025            storage_rebate: change_epoch.storage_rebate,
1026            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
1027            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1028            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
1029            // AdvanceEpochV1 does not use those fields, but keeping them to avoid creating a
1030            // separate AdvanceEpochParams struct.
1031            max_committee_members_count: 0,
1032            eligible_active_validators: vec![],
1033        };
1034        let advance_epoch_pt = construct_advance_epoch_pt_v1(builder, &params)?;
1035        advance_epoch_impl(
1036            advance_epoch_pt,
1037            params,
1038            change_epoch.system_packages,
1039            temporary_store,
1040            tx_ctx,
1041            move_vm,
1042            gas_charger,
1043            protocol_config,
1044            metrics,
1045            trace_builder_opt,
1046        )
1047    }
1048
1049    /// Advances the epoch for the given `ChangeEpochV2` transaction kind by
1050    /// constructing a programmable transaction, executing it and processing the
1051    /// system packages.
1052    fn advance_epoch_v2(
1053        builder: ProgrammableTransactionBuilder,
1054        change_epoch_v2: ChangeEpochV2,
1055        temporary_store: &mut TemporaryStore<'_>,
1056        tx_ctx: &mut TxContext,
1057        move_vm: &Arc<MoveVM>,
1058        gas_charger: &mut GasCharger,
1059        protocol_config: &ProtocolConfig,
1060        metrics: Arc<LimitsMetrics>,
1061        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1062    ) -> Result<(), ExecutionError> {
1063        let params = AdvanceEpochParams {
1064            epoch: change_epoch_v2.epoch,
1065            next_protocol_version: change_epoch_v2.protocol_version,
1066            validator_subsidy: protocol_config.validator_target_reward(),
1067            storage_charge: change_epoch_v2.storage_charge,
1068            computation_charge: change_epoch_v2.computation_charge,
1069            computation_charge_burned: change_epoch_v2.computation_charge_burned,
1070            storage_rebate: change_epoch_v2.storage_rebate,
1071            non_refundable_storage_fee: change_epoch_v2.non_refundable_storage_fee,
1072            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1073            epoch_start_timestamp_ms: change_epoch_v2.epoch_start_timestamp_ms,
1074            max_committee_members_count: protocol_config.max_committee_members_count(),
1075            // AdvanceEpochV2 does not use this field, but keeping them to avoid creating a
1076            // separate AdvanceEpochParams struct.
1077            eligible_active_validators: vec![],
1078        };
1079        let advance_epoch_pt = construct_advance_epoch_pt_v2(builder, &params)?;
1080        advance_epoch_impl(
1081            advance_epoch_pt,
1082            params,
1083            change_epoch_v2.system_packages,
1084            temporary_store,
1085            tx_ctx,
1086            move_vm,
1087            gas_charger,
1088            protocol_config,
1089            metrics,
1090            trace_builder_opt,
1091        )
1092    }
1093
1094    /// Advances the epoch for the given `ChangeEpochV3` transaction kind by
1095    /// constructing a programmable transaction, executing it and processing the
1096    /// system packages.
1097    fn advance_epoch_v3(
1098        builder: ProgrammableTransactionBuilder,
1099        change_epoch_v3: ChangeEpochV3,
1100        temporary_store: &mut TemporaryStore<'_>,
1101        tx_ctx: &mut TxContext,
1102        move_vm: &Arc<MoveVM>,
1103        gas_charger: &mut GasCharger,
1104        protocol_config: &ProtocolConfig,
1105        metrics: Arc<LimitsMetrics>,
1106        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1107    ) -> Result<(), ExecutionError> {
1108        let params = AdvanceEpochParams {
1109            epoch: change_epoch_v3.epoch,
1110            next_protocol_version: change_epoch_v3.protocol_version,
1111            validator_subsidy: protocol_config.validator_target_reward(),
1112            storage_charge: change_epoch_v3.storage_charge,
1113            computation_charge: change_epoch_v3.computation_charge,
1114            computation_charge_burned: change_epoch_v3.computation_charge_burned,
1115            storage_rebate: change_epoch_v3.storage_rebate,
1116            non_refundable_storage_fee: change_epoch_v3.non_refundable_storage_fee,
1117            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1118            epoch_start_timestamp_ms: change_epoch_v3.epoch_start_timestamp_ms,
1119            max_committee_members_count: protocol_config.max_committee_members_count(),
1120            eligible_active_validators: change_epoch_v3.eligible_active_validators,
1121        };
1122        let advance_epoch_pt = construct_advance_epoch_pt_v3(builder, &params)?;
1123        advance_epoch_impl(
1124            advance_epoch_pt,
1125            params,
1126            change_epoch_v3.system_packages,
1127            temporary_store,
1128            tx_ctx,
1129            move_vm,
1130            gas_charger,
1131            protocol_config,
1132            metrics,
1133            trace_builder_opt,
1134        )
1135    }
1136
1137    fn process_system_packages(
1138        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
1139        temporary_store: &mut TemporaryStore<'_>,
1140        tx_ctx: &mut TxContext,
1141        move_vm: &MoveVM,
1142        gas_charger: &mut GasCharger,
1143        protocol_config: &ProtocolConfig,
1144        metrics: Arc<LimitsMetrics>,
1145        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1146    ) {
1147        let binary_config = to_binary_config(protocol_config);
1148        for (version, modules, dependencies) in system_packages.into_iter() {
1149            let deserialized_modules: Vec<_> = modules
1150                .iter()
1151                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
1152                .collect();
1153
1154            if version == OBJECT_START_VERSION {
1155                let package_id = deserialized_modules.first().unwrap().address();
1156                info!("adding new system package {package_id}");
1157
1158                let publish_pt = {
1159                    let mut b = ProgrammableTransactionBuilder::new();
1160                    b.command(Command::Publish(modules, dependencies));
1161                    b.finish()
1162                };
1163
1164                programmable_transactions::execution::execute::<execution_mode::System>(
1165                    protocol_config,
1166                    metrics.clone(),
1167                    move_vm,
1168                    temporary_store,
1169                    tx_ctx,
1170                    gas_charger,
1171                    publish_pt,
1172                    trace_builder_opt,
1173                )
1174                .expect("System Package Publish must succeed");
1175            } else {
1176                let mut new_package = Object::new_system_package(
1177                    &deserialized_modules,
1178                    version,
1179                    dependencies,
1180                    tx_ctx.digest(),
1181                );
1182
1183                info!(
1184                    "upgraded system package {:?}",
1185                    new_package.compute_object_reference()
1186                );
1187
1188                // Decrement the version before writing the package so that the store can record
1189                // the version growing by one in the effects.
1190                new_package
1191                    .data
1192                    .try_as_package_mut()
1193                    .unwrap()
1194                    .decrement_version();
1195
1196                // upgrade of a previously existing framework module
1197                temporary_store.upgrade_system_package(new_package);
1198            }
1199        }
1200    }
1201
1202    /// Perform metadata updates in preparation for the transactions in the
1203    /// upcoming checkpoint:
1204    ///
1205    /// - Set the timestamp for the `Clock` shared object from the timestamp in
1206    ///   the header from consensus.
1207    fn setup_consensus_commit(
1208        consensus_commit_timestamp_ms: CheckpointTimestamp,
1209        temporary_store: &mut TemporaryStore<'_>,
1210        tx_ctx: &mut TxContext,
1211        move_vm: &Arc<MoveVM>,
1212        gas_charger: &mut GasCharger,
1213        protocol_config: &ProtocolConfig,
1214        metrics: Arc<LimitsMetrics>,
1215        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1216    ) -> Result<(), ExecutionError> {
1217        let pt = {
1218            let mut builder = ProgrammableTransactionBuilder::new();
1219            let res = builder.move_call(
1220                IOTA_FRAMEWORK_ADDRESS.into(),
1221                CLOCK_MODULE_NAME.to_owned(),
1222                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
1223                vec![],
1224                vec![
1225                    CallArg::CLOCK_MUT,
1226                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
1227                ],
1228            );
1229            assert_invariant!(
1230                res.is_ok(),
1231                "Unable to generate consensus_commit_prologue transaction!"
1232            );
1233            builder.finish()
1234        };
1235        programmable_transactions::execution::execute::<execution_mode::System>(
1236            protocol_config,
1237            metrics,
1238            move_vm,
1239            temporary_store,
1240            tx_ctx,
1241            gas_charger,
1242            pt,
1243            trace_builder_opt,
1244        )
1245    }
1246
1247    /// This function adds a Move call to the IOTA framework's
1248    /// `authenticator_state_create` function, preparing the transaction for
1249    /// execution.
1250    fn setup_authenticator_state_create(
1251        mut builder: ProgrammableTransactionBuilder,
1252    ) -> ProgrammableTransactionBuilder {
1253        builder
1254            .move_call(
1255                IOTA_FRAMEWORK_ADDRESS.into(),
1256                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1257                AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1258                vec![],
1259                vec![],
1260            )
1261            .expect("Unable to generate authenticator_state_create transaction!");
1262        builder
1263    }
1264
1265    /// Sets up and executes a `ProgrammableTransaction` to update the
1266    /// authenticator state. This function constructs a transaction that
1267    /// invokes the `authenticator_state_update` function from the IOTA
1268    /// framework, passing the authenticator state object and new active JWKS as
1269    /// arguments. It then executes the transaction using the system
1270    /// execution mode.
1271    fn setup_authenticator_state_update(
1272        update: AuthenticatorStateUpdateV1,
1273        temporary_store: &mut TemporaryStore<'_>,
1274        tx_ctx: &mut TxContext,
1275        move_vm: &Arc<MoveVM>,
1276        gas_charger: &mut GasCharger,
1277        protocol_config: &ProtocolConfig,
1278        metrics: Arc<LimitsMetrics>,
1279        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1280    ) -> Result<(), ExecutionError> {
1281        let pt = {
1282            let mut builder = ProgrammableTransactionBuilder::new();
1283            let res = builder.move_call(
1284                IOTA_FRAMEWORK_ADDRESS.into(),
1285                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1286                AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1287                vec![],
1288                vec![
1289                    CallArg::Object(ObjectArg::SharedObject {
1290                        id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1291                        initial_shared_version: update.authenticator_obj_initial_shared_version,
1292                        mutable: true,
1293                    }),
1294                    CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1295                ],
1296            );
1297            assert_invariant!(
1298                res.is_ok(),
1299                "Unable to generate authenticator_state_update transaction!"
1300            );
1301            builder.finish()
1302        };
1303        programmable_transactions::execution::execute::<execution_mode::System>(
1304            protocol_config,
1305            metrics,
1306            move_vm,
1307            temporary_store,
1308            tx_ctx,
1309            gas_charger,
1310            pt,
1311            trace_builder_opt,
1312        )
1313    }
1314
1315    /// Configures a `ProgrammableTransactionBuilder` to expire authenticator
1316    /// state by invoking the `authenticator_state_expire_jwks` function
1317    /// from the IOTA framework. The function adds the necessary Move call
1318    /// with the authenticator state object and the minimum epoch as arguments.
1319    fn setup_authenticator_state_expire(
1320        mut builder: ProgrammableTransactionBuilder,
1321        expire: AuthenticatorStateExpire,
1322    ) -> ProgrammableTransactionBuilder {
1323        builder
1324            .move_call(
1325                IOTA_FRAMEWORK_ADDRESS.into(),
1326                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1327                AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1328                vec![],
1329                vec![
1330                    CallArg::Object(ObjectArg::SharedObject {
1331                        id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1332                        initial_shared_version: expire.authenticator_obj_initial_shared_version,
1333                        mutable: true,
1334                    }),
1335                    CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1336                ],
1337            )
1338            .expect("Unable to generate authenticator_state_expire transaction!");
1339        builder
1340    }
1341
1342    /// The function constructs a transaction that invokes
1343    /// the `randomness_state_update` function from the IOTA framework,
1344    /// passing the randomness state object, the `randomness_round`,
1345    /// and the `random_bytes` as arguments. It then executes the transaction
1346    /// using the system execution mode.
1347    fn setup_randomness_state_update(
1348        update: RandomnessStateUpdate,
1349        temporary_store: &mut TemporaryStore<'_>,
1350        tx_ctx: &mut TxContext,
1351        move_vm: &Arc<MoveVM>,
1352        gas_charger: &mut GasCharger,
1353        protocol_config: &ProtocolConfig,
1354        metrics: Arc<LimitsMetrics>,
1355        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1356    ) -> Result<(), ExecutionError> {
1357        let pt = {
1358            let mut builder = ProgrammableTransactionBuilder::new();
1359            let res = builder.move_call(
1360                IOTA_FRAMEWORK_ADDRESS.into(),
1361                RANDOMNESS_MODULE_NAME.to_owned(),
1362                RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1363                vec![],
1364                vec![
1365                    CallArg::Object(ObjectArg::SharedObject {
1366                        id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1367                        initial_shared_version: update.randomness_obj_initial_shared_version,
1368                        mutable: true,
1369                    }),
1370                    CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1371                    CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1372                ],
1373            );
1374            assert_invariant!(
1375                res.is_ok(),
1376                "Unable to generate randomness_state_update transaction!"
1377            );
1378            builder.finish()
1379        };
1380        programmable_transactions::execution::execute::<execution_mode::System>(
1381            protocol_config,
1382            metrics,
1383            move_vm,
1384            temporary_store,
1385            tx_ctx,
1386            gas_charger,
1387            pt,
1388            trace_builder_opt,
1389        )
1390    }
1391}