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::{
11        collections::{BTreeMap, BTreeSet, HashSet},
12        sync::Arc,
13    };
14
15    use iota_move_natives::all_natives;
16    use iota_protocol_config::{LimitThresholdCrossed, ProtocolConfig, check_limit_by_meter};
17    #[cfg(msim)]
18    use iota_types::iota_system_state::advance_epoch_result_injection::maybe_modify_result;
19    use iota_types::{
20        IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS, IOTA_FRAMEWORK_PACKAGE_ID,
21        IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID, Identifier,
22        account_abstraction::authenticator_function::{
23            AuthenticatorFunctionRef, AuthenticatorFunctionRefForExecution,
24            AuthenticatorFunctionRefV1,
25        },
26        auth_context::AuthContext,
27        authenticator_state::{
28            AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME,
29            AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME, AUTHENTICATOR_STATE_MODULE_NAME,
30            AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
31        },
32        balance::{
33            BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
34            BALANCE_MODULE_NAME,
35        },
36        base_types::{
37            IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, TxContext,
38        },
39        clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME},
40        committee::EpochId,
41        effects::TransactionEffects,
42        error::{ExecutionError, ExecutionErrorKind},
43        execution::{ExecutionResults, ExecutionResultsV1, SharedInput, is_certificate_denied},
44        execution_config_utils::to_binary_config,
45        execution_status::{CongestedObjects, ExecutionStatus},
46        gas::{GasCostSummary, IotaGasStatus},
47        gas_coin::GAS,
48        inner_temporary_store::InnerTemporaryStore,
49        iota_system_state::{
50            ADVANCE_EPOCH_FUNCTION_NAME, AdvanceEpochParams, IOTA_SYSTEM_MODULE_NAME,
51        },
52        messages_checkpoint::CheckpointTimestamp,
53        metrics::LimitsMetrics,
54        move_authenticator::MoveAuthenticator,
55        object::{OBJECT_START_VERSION, Object, ObjectInner},
56        programmable_transaction_builder::ProgrammableTransactionBuilder,
57        randomness_state::{RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_UPDATE_FUNCTION_NAME},
58        storage::{BackingStore, Storage},
59        transaction::{
60            Argument, AuthenticatorStateExpire, AuthenticatorStateUpdateV1, CallArg, ChangeEpoch,
61            ChangeEpochV2, ChangeEpochV3, ChangeEpochV4, CheckedInputObjects, Command,
62            EndOfEpochTransactionKind, GenesisTransaction, InputObjects, ObjectArg,
63            ProgrammableTransaction, RandomnessStateUpdate, TransactionKind,
64        },
65    };
66    use move_binary_format::CompiledModule;
67    use move_trace_format::format::MoveTraceBuilder;
68    use move_vm_runtime::move_vm::MoveVM;
69    use tracing::{info, instrument, trace, warn};
70
71    use crate::{
72        adapter::new_move_vm,
73        execution_mode::{self, ExecutionMode},
74        gas_charger::GasCharger,
75        programmable_transactions,
76        temporary_store::TemporaryStore,
77        type_layout_resolver::TypeLayoutResolver,
78    };
79
80    /// The main entry point to the adapter's transaction execution. It
81    /// prepares a transaction for execution, then executes it through an
82    /// inner execution method and finally produces an instance of
83    /// transaction effects. It also returns the inner temporary store, which
84    /// contains the objects resulting from the transaction execution, the gas
85    /// status instance, which tracks the gas usage, and the execution result.
86    /// The function handles transaction execution based on the provided
87    /// `TransactionKind`. It checks for any expensive operations, manages
88    /// shared object references, and ensures transaction dependencies are
89    /// met. The returned objects are not committed to the store until the
90    /// resulting effects are applied by the caller.
91    #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
92    pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
93        store: &dyn BackingStore,
94        input_objects: CheckedInputObjects,
95        gas_coins: Vec<ObjectRef>,
96        gas_status: IotaGasStatus,
97        transaction_kind: TransactionKind,
98        transaction_signer: IotaAddress,
99        transaction_digest: TransactionDigest,
100        move_vm: &Arc<MoveVM>,
101        epoch_id: &EpochId,
102        epoch_timestamp_ms: u64,
103        protocol_config: &ProtocolConfig,
104        metrics: Arc<LimitsMetrics>,
105        enable_expensive_checks: bool,
106        certificate_deny_set: &HashSet<TransactionDigest>,
107        trace_builder_opt: &mut Option<MoveTraceBuilder>,
108    ) -> (
109        InnerTemporaryStore,
110        IotaGasStatus,
111        TransactionEffects,
112        Result<Mode::ExecutionResults, ExecutionError>,
113    ) {
114        let input_objects = input_objects.into_inner();
115        let mutable_inputs = if enable_expensive_checks {
116            input_objects.mutable_inputs().keys().copied().collect()
117        } else {
118            HashSet::new()
119        };
120        let shared_object_refs = input_objects.filter_shared_objects();
121        let receiving_objects = transaction_kind.receiving_objects();
122        let transaction_dependencies = input_objects.transaction_dependencies();
123        let contains_deleted_input = input_objects.contains_deleted_objects();
124        let cancelled_objects = input_objects.get_cancelled_objects();
125
126        let temporary_store = TemporaryStore::new(
127            store,
128            input_objects,
129            receiving_objects,
130            transaction_digest,
131            protocol_config,
132            *epoch_id,
133        );
134
135        let gas_charger =
136            GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
137
138        let mut tx_ctx = TxContext::new_from_components(
139            &transaction_signer,
140            &transaction_digest,
141            epoch_id,
142            epoch_timestamp_ms,
143        );
144
145        execute_transaction_to_effects_inner::<Mode>(
146            temporary_store,
147            gas_charger,
148            &mut tx_ctx,
149            &mutable_inputs,
150            shared_object_refs,
151            transaction_dependencies,
152            contains_deleted_input,
153            cancelled_objects,
154            transaction_kind,
155            transaction_signer,
156            transaction_digest,
157            move_vm,
158            epoch_id,
159            protocol_config,
160            metrics,
161            enable_expensive_checks,
162            certificate_deny_set,
163            trace_builder_opt,
164            None,
165        )
166    }
167
168    /// The main execution function that processes a transaction and produces
169    /// effects. It handles gas charging and execution logic.
170    #[instrument(name = "tx_execute_to_effects_inner", level = "debug", skip_all)]
171    fn execute_transaction_to_effects_inner<Mode: ExecutionMode>(
172        mut temporary_store: TemporaryStore,
173        mut gas_charger: GasCharger,
174        tx_ctx: &mut TxContext,
175        mutable_inputs: &HashSet<ObjectID>,
176        shared_object_refs: Vec<SharedInput>,
177        mut transaction_dependencies: BTreeSet<TransactionDigest>,
178        contains_deleted_input: bool,
179        cancelled_objects: Option<(Vec<ObjectID>, SequenceNumber)>,
180        transaction_kind: TransactionKind,
181        transaction_signer: IotaAddress,
182        transaction_digest: TransactionDigest,
183        move_vm: &Arc<MoveVM>,
184        epoch_id: &EpochId,
185        protocol_config: &ProtocolConfig,
186        metrics: Arc<LimitsMetrics>,
187        enable_expensive_checks: bool,
188        certificate_deny_set: &HashSet<TransactionDigest>,
189        trace_builder_opt: &mut Option<MoveTraceBuilder>,
190        pre_execution_result_opt: Option<
191            Result<
192                <execution_mode::Authentication as ExecutionMode>::ExecutionResults,
193                ExecutionError,
194            >,
195        >,
196    ) -> (
197        InnerTemporaryStore,
198        IotaGasStatus,
199        TransactionEffects,
200        Result<Mode::ExecutionResults, ExecutionError>,
201    ) {
202        let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
203        let deny_cert = is_certificate_denied(&transaction_digest, certificate_deny_set);
204
205        let (gas_cost_summary, execution_result) = execute_transaction::<Mode>(
206            &mut temporary_store,
207            transaction_kind,
208            &mut gas_charger,
209            tx_ctx,
210            move_vm,
211            protocol_config,
212            metrics,
213            enable_expensive_checks,
214            deny_cert,
215            contains_deleted_input,
216            cancelled_objects,
217            trace_builder_opt,
218            pre_execution_result_opt,
219        );
220
221        let status = if let Err(error) = &execution_result {
222            elaborate_error_logs(error, transaction_digest)
223        } else {
224            ExecutionStatus::Success
225        };
226
227        #[skip_checked_arithmetic]
228        trace!(
229            tx_digest = ?transaction_digest,
230            computation_gas_cost = gas_cost_summary.computation_cost,
231            computation_gas_cost_burned = gas_cost_summary.computation_cost_burned,
232            storage_gas_cost = gas_cost_summary.storage_cost,
233            storage_gas_rebate = gas_cost_summary.storage_rebate,
234            "Finished execution of transaction with status {:?}",
235            status
236        );
237
238        // Genesis writes a special digest to indicate that an object was created during
239        // genesis and not written by any normal transaction - remove that from the
240        // dependencies
241        transaction_dependencies.remove(&TransactionDigest::genesis_marker());
242
243        if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
244            temporary_store
245                .check_ownership_invariants(
246                    &transaction_signer,
247                    &mut gas_charger,
248                    mutable_inputs,
249                    is_epoch_change,
250                )
251                .unwrap()
252        } // else, in dev inspect mode and anything goes--don't check
253
254        let (inner, effects) = temporary_store.into_effects(
255            shared_object_refs,
256            &transaction_digest,
257            transaction_dependencies,
258            gas_cost_summary,
259            status,
260            &mut gas_charger,
261            *epoch_id,
262        );
263
264        (
265            inner,
266            gas_charger.into_gas_status(),
267            effects,
268            execution_result,
269        )
270    }
271
272    /// This function produces transaction effects for a transaction that
273    /// requires the Move authentication.
274    /// It creates a temporary store, gas charger, and transaction context for
275    /// the authentication execution and then reuses these for the normal
276    /// transaction execution.
277    /// Running the Move authentication can have two outcomes:
278    ///   - If it fails, then it charges gas for the failed execution of the
279    ///     authentication and produces transaction effects with the appropriate
280    ///     error status.
281    ///   - Else, if the authentication is successful, it continues with the
282    ///     normal transaction execution.
283    /// It combines the input objects from both the authentication and
284    /// transaction.
285    #[instrument(
286        name = "tx_authenticate_then_execute_to_effects",
287        level = "debug",
288        skip_all
289    )]
290    pub fn authenticate_then_execute_transaction_to_effects<Mode: ExecutionMode>(
291        store: &dyn BackingStore,
292        // Configuration
293        protocol_config: &ProtocolConfig,
294        metrics: Arc<LimitsMetrics>,
295        enable_expensive_checks: bool,
296        certificate_deny_set: &HashSet<TransactionDigest>,
297        // Epoch
298        epoch_id: &EpochId,
299        epoch_timestamp_ms: u64,
300        // Gas related
301        gas_status: IotaGasStatus,
302        gas_coins: Vec<ObjectRef>,
303        // Authenticator
304        authenticator: MoveAuthenticator,
305        authenticator_function_ref_for_execution: AuthenticatorFunctionRefForExecution,
306        authenticator_input_objects: CheckedInputObjects,
307        authenticator_and_transaction_input_objects: CheckedInputObjects,
308        // Transaction
309        transaction_kind: TransactionKind,
310        transaction_signer: IotaAddress,
311        transaction_digest: TransactionDigest,
312        // Tracing
313        trace_builder_opt: &mut Option<MoveTraceBuilder>,
314        // VM
315        move_vm: &Arc<MoveVM>,
316    ) -> (
317        InnerTemporaryStore,
318        IotaGasStatus,
319        TransactionEffects,
320        Result<Mode::ExecutionResults, ExecutionError>,
321    ) {
322        // Preparation
323        // It involves setting up the TemporaryStore, GasCharger, and TxContext, that
324        // will be common for both the authentication and transaction execution.
325
326        // Input objects come from both authentication and transaction inputs
327        let input_objects = authenticator_and_transaction_input_objects.into_inner();
328        // Mutable inputs come only from the transaction inputs
329        let mutable_inputs = if enable_expensive_checks {
330            input_objects.mutable_inputs().keys().copied().collect()
331        } else {
332            HashSet::new()
333        };
334        // Shared object refs come from both authentication and transaction inputs
335        let shared_object_refs = input_objects.filter_shared_objects();
336        // Receiving objects can only come from the transaction inputs
337        let transaction_receiving_objects = transaction_kind.receiving_objects();
338        // Transaction dependencies come from both authentication and transaction inputs
339        let transaction_dependencies = input_objects.transaction_dependencies();
340        // Deleted and cancelled objects come from both authentication and transaction
341        // inputs
342        let contains_deleted_input = input_objects.contains_deleted_objects();
343        let cancelled_objects = input_objects.get_cancelled_objects();
344
345        // Prepare the temporary store.
346        let mut temporary_store = TemporaryStore::new(
347            store,
348            input_objects,
349            transaction_receiving_objects,
350            transaction_digest,
351            protocol_config,
352            *epoch_id,
353        );
354
355        // Prepare the gas charger.
356        let mut gas_charger =
357            GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
358
359        // Prepare the transaction context.
360        let mut tx_ctx = TxContext::new_from_components(
361            &transaction_signer,
362            &transaction_digest,
363            epoch_id,
364            epoch_timestamp_ms,
365        );
366
367        // Authentication execution.
368        // It does not alter the state, if not for command execution gas charging, and
369        // produces no effects other than possible errors.
370
371        let AuthenticatorFunctionRefForExecution {
372            authenticator_function_ref,
373            loaded_object_id,
374            loaded_object_metadata,
375        } = authenticator_function_ref_for_execution;
376
377        let authentication_execution_result = match authenticator_function_ref {
378            AuthenticatorFunctionRef::V1(authenticator_function_ref_v1) => {
379                // Save the loaded object metadata, i.e., the field object containing the
380                // AuthenticatorFunctionRef, in the temporary store.
381                temporary_store.save_loaded_runtime_objects(BTreeMap::from([(
382                    loaded_object_id,
383                    loaded_object_metadata,
384                )]));
385
386                // Run the authentication execution.
387                authenticate_transaction_inner(
388                    &mut temporary_store,
389                    protocol_config,
390                    metrics.clone(),
391                    &mut gas_charger,
392                    authenticator,
393                    authenticator_function_ref_v1,
394                    &authenticator_input_objects.into_inner(),
395                    transaction_kind.clone(),
396                    transaction_digest,
397                    &mut tx_ctx,
398                    trace_builder_opt,
399                    move_vm,
400                )
401            }
402        };
403
404        // Transaction execution.
405        // At this stage we arrive with gas charged for the execution of the
406        // authenticate function and a result which is either empty or an error.
407        // We can now start the creation of the transaction effects, either for an
408        // authentication failure or for a normal execution of the transaction.
409
410        // Run the transaction execution and return the effects.
411        execute_transaction_to_effects_inner::<Mode>(
412            temporary_store,
413            gas_charger,
414            &mut tx_ctx,
415            &mutable_inputs,
416            shared_object_refs,
417            transaction_dependencies,
418            contains_deleted_input,
419            cancelled_objects,
420            transaction_kind,
421            transaction_signer,
422            transaction_digest,
423            move_vm,
424            epoch_id,
425            protocol_config,
426            metrics,
427            enable_expensive_checks,
428            certificate_deny_set,
429            trace_builder_opt,
430            Some(authentication_execution_result),
431        )
432    }
433
434    /// This function checks the authentication of a transaction without
435    /// returning effects. It executes an authenticate function using the
436    /// information of an authenticator. If the execution fails, it returns
437    /// an execution error; otherwise it returns an empty value.
438    #[instrument(name = "tx_validate", level = "debug", skip_all)]
439    pub fn authenticate_transaction(
440        store: &dyn BackingStore,
441        // Configuration
442        protocol_config: &ProtocolConfig,
443        metrics: Arc<LimitsMetrics>,
444        // Epoch
445        epoch_id: &EpochId,
446        epoch_timestamp_ms: u64,
447        // Gas related
448        gas_status: IotaGasStatus,
449        // Authenticator
450        authenticator: MoveAuthenticator,
451        authenticator_function_ref: AuthenticatorFunctionRef,
452        authenticator_input_objects: CheckedInputObjects,
453        // Transaction
454        transaction_kind: TransactionKind,
455        transaction_signer: IotaAddress,
456        transaction_digest: TransactionDigest,
457        // Tracing
458        trace_builder_opt: &mut Option<MoveTraceBuilder>,
459        // VM
460        move_vm: &Arc<MoveVM>,
461    ) -> Result<<execution_mode::Authentication as ExecutionMode>::ExecutionResults, ExecutionError>
462    {
463        let input_objects = authenticator_input_objects.into_inner();
464
465        // Prepare the temporary store for the authentication execution.
466        let mut temporary_store = TemporaryStore::new(
467            store,
468            input_objects.clone(),
469            vec![],
470            transaction_digest,
471            protocol_config,
472            *epoch_id,
473        );
474
475        // Prepare the gas charger for authentication execution.
476        let mut gas_charger =
477            GasCharger::new(transaction_digest, vec![], gas_status, protocol_config);
478
479        // Prepare the transaction context, equal for both authentication and
480        // transaction execution.
481        let mut tx_ctx = TxContext::new_from_components(
482            &transaction_signer,
483            &transaction_digest,
484            epoch_id,
485            epoch_timestamp_ms,
486        );
487
488        // Run the authentication.
489        match authenticator_function_ref {
490            AuthenticatorFunctionRef::V1(authenticator_function_ref_v1) => {
491                authenticate_transaction_inner(
492                    &mut temporary_store,
493                    protocol_config,
494                    metrics.clone(),
495                    &mut gas_charger,
496                    authenticator,
497                    authenticator_function_ref_v1,
498                    &input_objects,
499                    transaction_kind.clone(),
500                    transaction_digest,
501                    &mut tx_ctx,
502                    trace_builder_opt,
503                    move_vm,
504                )
505            }
506        }
507    }
508
509    // This function implements the authentication execution. It checks that the
510    // authentication method used by the authenticator is valid. It prepares a
511    /// `MoveAuthenticator` PTB with a single move call for execution, then
512    /// executes it through an inner execution method. The
513    /// `MoveAuthenticator` provides the inputs to use for the
514    /// authentication function found in `AuthenticatorFunctionRef`,
515    /// that is retrieved from an account.
516    /// If the execution fails, it returns an execution error; otherwise it
517    /// returns an empty value.
518    #[instrument(name = "tx_validate", level = "debug", skip_all)]
519    pub fn authenticate_transaction_inner(
520        temporary_store: &mut TemporaryStore<'_>,
521        // Configuration
522        protocol_config: &ProtocolConfig,
523        metrics: Arc<LimitsMetrics>,
524        // Gas related
525        gas_charger: &mut GasCharger,
526        // Authenticator
527        authenticator: MoveAuthenticator,
528        authenticator_function_ref: AuthenticatorFunctionRefV1,
529        authenticator_input_objects: &InputObjects,
530        // Transaction
531        transaction_kind: TransactionKind,
532        transaction_digest: TransactionDigest,
533        tx_ctx: &mut TxContext,
534        // Tracing
535        trace_builder_opt: &mut Option<MoveTraceBuilder>,
536        // VM
537        move_vm: &Arc<MoveVM>,
538    ) -> Result<<execution_mode::Authentication as ExecutionMode>::ExecutionResults, ExecutionError>
539    {
540        // Check the preconditions.
541        debug_assert!(
542            transaction_kind.is_programmable_transaction(),
543            "Only programmable transactions are allowed"
544        );
545        debug_assert!(
546            authenticator_input_objects
547                .mutable_inputs()
548                .keys()
549                .copied()
550                .collect::<HashSet<_>>()
551                .is_empty(),
552            "No mutable inputs are allowed"
553        );
554        debug_assert!(
555            authenticator.receiving_objects().is_empty(),
556            "No receiving inputs are allowed"
557        );
558
559        let contains_deleted_input = authenticator_input_objects.contains_deleted_objects();
560        let cancelled_objects = authenticator_input_objects.get_cancelled_objects();
561
562        // Prepare the authentication context.
563        let auth_ctx = {
564            let TransactionKind::ProgrammableTransaction(ptb) = &transaction_kind else {
565                unreachable!("Only programmable transactions are allowed");
566            };
567            AuthContext::new_from_components(authenticator.digest(), ptb)
568        };
569
570        // Store the authentication context in the temporary store.
571        // It will be added to the authentication's parameter list later, just before
572        // execution.
573        temporary_store.store_auth_context(auth_ctx);
574
575        // Execute the authentication.
576        let authentication_execution_result = execute_authenticator_move_call(
577            temporary_store,
578            authenticator,
579            authenticator_function_ref,
580            gas_charger,
581            tx_ctx,
582            move_vm,
583            protocol_config,
584            metrics.clone(),
585            false,
586            contains_deleted_input,
587            cancelled_objects,
588            trace_builder_opt,
589        );
590
591        // Check the authentication result.
592        let authentication_execution_status = if let Err(error) = &authentication_execution_result {
593            elaborate_error_logs(error, transaction_digest)
594        } else {
595            ExecutionStatus::Success
596        };
597
598        #[skip_checked_arithmetic]
599        trace!(
600            tx_digest = ?transaction_digest,
601            computation_gas_cost = gas_charger.summary().gas_used(),
602            "Finished authenticator execution of transaction with status {:?}",
603            authentication_execution_status
604        );
605
606        authentication_execution_result
607    }
608
609    /// Executes an authentication move call by processing the specified
610    /// `ProgrammableTransaction`, running the main execution logic.
611    /// Similarly to `execute_transaction`, this function handles certain error
612    /// conditions such as denied certificate, deleted input objects failed
613    /// consistency checks.
614    ///
615    /// Gas costs are managed through the `GasCharger` argument and charged only
616    /// for authentication move function execution.
617    ///
618    /// Returns only the execution results.
619    #[instrument(name = "auth_execute", level = "debug", skip_all)]
620    fn execute_authenticator_move_call(
621        temporary_store: &mut TemporaryStore<'_>,
622        authenticator: MoveAuthenticator,
623        authenticator_function_ref: AuthenticatorFunctionRefV1,
624        gas_charger: &mut GasCharger,
625        tx_ctx: &mut TxContext,
626        move_vm: &Arc<MoveVM>,
627        protocol_config: &ProtocolConfig,
628        metrics: Arc<LimitsMetrics>,
629        deny_cert: bool,
630        contains_deleted_input: bool,
631        cancelled_objects: Option<(Vec<ObjectID>, SequenceNumber)>,
632        trace_builder_opt: &mut Option<MoveTraceBuilder>,
633    ) -> Result<<execution_mode::Authentication as ExecutionMode>::ExecutionResults, ExecutionError>
634    {
635        debug_assert!(
636            gas_charger.no_charges(),
637            "At this point no gas charges must be applied yet"
638        );
639
640        // It must NOT charge gas for reading the Move authenticator input objects from
641        // the storage. It will be done later during the transaction execution.
642        // Then execute the authentication.
643        run_inputs_checks(
644            protocol_config,
645            deny_cert,
646            contains_deleted_input,
647            cancelled_objects,
648        )
649        .and_then(|()| {
650            let authenticator_move_call =
651                setup_authenticator_move_call(authenticator, authenticator_function_ref)?;
652            programmable_transactions::execution::execute::<execution_mode::Authentication>(
653                protocol_config,
654                metrics.clone(),
655                move_vm,
656                temporary_store,
657                tx_ctx,
658                gas_charger,
659                authenticator_move_call,
660                trace_builder_opt,
661            )
662            .and_then(|ok_result| {
663                temporary_store.check_move_authenticator_results_consistency()?;
664                Ok(ok_result)
665            })
666        })
667    }
668
669    /// Function dedicated to the execution of a GenesisTransaction.
670    /// The function creates an `InnerTemporaryStore`, processes the input
671    /// objects, and executes the transaction in unmetered mode using the
672    /// `Genesis` execution mode. It returns an inner temporary store that
673    /// contains the objects found into the input `GenesisTransaction` by
674    /// adding the data for `previous_transaction` and `storage_rebate` fields.
675    pub fn execute_genesis_state_update(
676        store: &dyn BackingStore,
677        protocol_config: &ProtocolConfig,
678        metrics: Arc<LimitsMetrics>,
679        move_vm: &Arc<MoveVM>,
680        tx_context: &mut TxContext,
681        input_objects: CheckedInputObjects,
682        pt: ProgrammableTransaction,
683    ) -> Result<InnerTemporaryStore, ExecutionError> {
684        let input_objects = input_objects.into_inner();
685        let mut temporary_store = TemporaryStore::new(
686            store,
687            input_objects,
688            vec![],
689            tx_context.digest(),
690            protocol_config,
691            0,
692        );
693        let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
694        programmable_transactions::execution::execute::<execution_mode::Genesis>(
695            protocol_config,
696            metrics,
697            move_vm,
698            &mut temporary_store,
699            tx_context,
700            &mut gas_charger,
701            pt,
702            &mut None,
703        )?;
704        temporary_store.update_object_version_and_prev_tx();
705        Ok(temporary_store.into_inner())
706    }
707
708    /// Executes a transaction by processing the specified `TransactionKind`,
709    /// applying the necessary gas charges and running the main execution logic.
710    /// The function handles certain error conditions such as denied
711    /// certificate, deleted input objects, exceeded execution meter limits,
712    /// failed conservation checks. It also accounts for unmetered storage
713    /// rebates and adjusts for special cases like epoch change
714    /// transactions. Gas costs are managed through the `GasCharger`
715    /// argument; gas is also charged in case of errors.
716    #[instrument(name = "tx_execute", level = "debug", skip_all)]
717    fn execute_transaction<Mode: ExecutionMode>(
718        temporary_store: &mut TemporaryStore<'_>,
719        transaction_kind: TransactionKind,
720        gas_charger: &mut GasCharger,
721        tx_ctx: &mut TxContext,
722        move_vm: &Arc<MoveVM>,
723        protocol_config: &ProtocolConfig,
724        metrics: Arc<LimitsMetrics>,
725        enable_expensive_checks: bool,
726        deny_cert: bool,
727        contains_deleted_input: bool,
728        cancelled_objects: Option<(Vec<ObjectID>, SequenceNumber)>,
729        trace_builder_opt: &mut Option<MoveTraceBuilder>,
730        pre_execution_result_opt: Option<
731            Result<
732                <execution_mode::Authentication as ExecutionMode>::ExecutionResults,
733                ExecutionError,
734            >,
735        >,
736    ) -> (
737        GasCostSummary,
738        Result<Mode::ExecutionResults, ExecutionError>,
739    ) {
740        gas_charger.smash_gas(temporary_store);
741
742        // At this point, either no charges have been applied yet or we have
743        // already a pre execution result to handle.
744        debug_assert!(
745            pre_execution_result_opt.is_some() || gas_charger.no_charges(),
746            "No gas charges must be applied yet"
747        );
748
749        let is_genesis_or_epoch_change_tx = matches!(transaction_kind, TransactionKind::Genesis(_))
750            || transaction_kind.is_end_of_epoch_tx();
751
752        let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
753
754        // We must charge object read here during transaction execution, because if this
755        // fails we must still ensure an effect is committed and all objects
756        // versions incremented
757        let result = gas_charger.charge_input_objects(temporary_store);
758        let mut result = result.and_then(|()| {
759            run_inputs_checks(
760                protocol_config,
761                deny_cert,
762                contains_deleted_input,
763                cancelled_objects,
764            )?;
765
766            // If the pre-execution succeeded, proceed with the main execution loop
767            // else propagate the pre-execution error
768            let mut execution_result = pre_execution_result_opt.unwrap_or(Ok(())).and_then(|_| {
769                execution_loop::<Mode>(
770                    temporary_store,
771                    transaction_kind,
772                    tx_ctx,
773                    move_vm,
774                    gas_charger,
775                    protocol_config,
776                    metrics.clone(),
777                    trace_builder_opt,
778                )
779            });
780
781            let meter_check = check_meter_limit(
782                temporary_store,
783                gas_charger,
784                protocol_config,
785                metrics.clone(),
786            );
787            if let Err(e) = meter_check {
788                execution_result = Err(e);
789            }
790
791            if execution_result.is_ok() {
792                let gas_check = check_written_objects_limit(
793                    temporary_store,
794                    gas_charger,
795                    protocol_config,
796                    metrics,
797                );
798                if let Err(e) = gas_check {
799                    execution_result = Err(e);
800                }
801            }
802
803            execution_result
804        });
805
806        let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
807        // For advance epoch transaction, we need to provide epoch rewards and rebates
808        // as extra information provided to check_iota_conserved, because we
809        // mint rewards, and burn the rebates. We also need to pass in the
810        // unmetered_storage_rebate because storage rebate is not reflected in
811        // the storage_rebate of gas summary. This is a bit confusing.
812        // We could probably clean up the code a bit.
813        // Put all the storage rebate accumulated in the system transaction
814        // to the 0x5 object so that it's not lost.
815        temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
816
817        if let Err(e) = run_conservation_checks::<Mode>(
818            temporary_store,
819            gas_charger,
820            tx_ctx,
821            move_vm,
822            enable_expensive_checks,
823            &cost_summary,
824            is_genesis_or_epoch_change_tx,
825            advance_epoch_gas_summary,
826        ) {
827            // FIXME: we cannot fail the transaction if this is an epoch change transaction.
828            result = Err(e);
829        }
830
831        (cost_summary, result)
832    }
833
834    /// Elaborate errors in logs if they are unexpected or their status is
835    /// terse.
836    fn elaborate_error_logs(
837        execution_error: &ExecutionError,
838        transaction_digest: TransactionDigest,
839    ) -> ExecutionStatus {
840        use ExecutionErrorKind as K;
841        match execution_error.kind() {
842            K::InvariantViolation | K::VMInvariantViolation => {
843                #[skip_checked_arithmetic]
844                tracing::error!(
845                    kind = ?execution_error.kind(),
846                    tx_digest = ?transaction_digest,
847                    "INVARIANT VIOLATION! Source: {:?}",
848                    execution_error.source(),
849                );
850            }
851
852            K::IotaMoveVerificationError | K::VMVerificationOrDeserializationError => {
853                #[skip_checked_arithmetic]
854                tracing::debug!(
855                    kind = ?execution_error.kind(),
856                    tx_digest = ?transaction_digest,
857                    "Verification Error. Source: {:?}",
858                    execution_error.source(),
859                );
860            }
861
862            K::PublishUpgradeMissingDependency | K::PublishUpgradeDependencyDowngrade => {
863                #[skip_checked_arithmetic]
864                tracing::debug!(
865                    kind = ?execution_error.kind(),
866                    tx_digest = ?transaction_digest,
867                    "Publish/Upgrade Error. Source: {:?}",
868                    execution_error.source(),
869                )
870            }
871
872            _ => (),
873        };
874
875        let (status, command) = execution_error.to_execution_status();
876        ExecutionStatus::new_failure(status, command)
877    }
878
879    /// Performs IOTA conservation checks during transaction execution, ensuring
880    /// that the transaction does not create or destroy IOTA. If
881    /// conservation is violated, the function attempts to recover
882    /// by resetting the gas charger, recharging gas, and rechecking
883    /// conservation. If recovery fails, it panics to avoid IOTA creation or
884    /// destruction. These checks include both simple and expensive
885    /// checks based on the configuration and are skipped for genesis or epoch
886    /// change transactions.
887    #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
888    fn run_conservation_checks<Mode: ExecutionMode>(
889        temporary_store: &mut TemporaryStore<'_>,
890        gas_charger: &mut GasCharger,
891        tx_ctx: &mut TxContext,
892        move_vm: &Arc<MoveVM>,
893        enable_expensive_checks: bool,
894        cost_summary: &GasCostSummary,
895        is_genesis_or_epoch_change_tx: bool,
896        advance_epoch_gas_summary: Option<(u64, u64)>,
897    ) -> Result<(), ExecutionError> {
898        let mut result: std::result::Result<(), iota_types::error::ExecutionError> = Ok(());
899        if !is_genesis_or_epoch_change_tx && !Mode::skip_conservation_checks() {
900            // ensure that this transaction did not create or destroy IOTA, try to recover
901            // if the check fails
902            let conservation_result = {
903                temporary_store
904                    .check_iota_conserved(cost_summary)
905                    .and_then(|()| {
906                        if enable_expensive_checks {
907                            // ensure that this transaction did not create or destroy IOTA, try to
908                            // recover if the check fails
909                            let mut layout_resolver =
910                                TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
911                            temporary_store.check_iota_conserved_expensive(
912                                cost_summary,
913                                advance_epoch_gas_summary,
914                                &mut layout_resolver,
915                            )
916                        } else {
917                            Ok(())
918                        }
919                    })
920            };
921            if let Err(conservation_err) = conservation_result {
922                // conservation violated. try to avoid panic by dumping all writes, charging for
923                // gas, re-checking conservation, and surfacing an aborted
924                // transaction with an invariant violation if all of that works
925                result = Err(conservation_err);
926                gas_charger.reset(temporary_store);
927                gas_charger.charge_gas(temporary_store, &mut result);
928                // check conservation once more
929                if let Err(recovery_err) = {
930                    temporary_store
931                        .check_iota_conserved(cost_summary)
932                        .and_then(|()| {
933                            if enable_expensive_checks {
934                                // ensure that this transaction did not create or destroy IOTA, try
935                                // to recover if the check fails
936                                let mut layout_resolver =
937                                    TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
938                                temporary_store.check_iota_conserved_expensive(
939                                    cost_summary,
940                                    advance_epoch_gas_summary,
941                                    &mut layout_resolver,
942                                )
943                            } else {
944                                Ok(())
945                            }
946                        })
947                } {
948                    // if we still fail, it's a problem with gas
949                    // charging that happens even in the "aborted" case--no other option but panic.
950                    // we will create or destroy IOTA otherwise
951                    panic!(
952                        "IOTA conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
953                        tx_ctx.digest(),
954                        recovery_err,
955                        gas_charger.summary()
956                    )
957                }
958            }
959        } // else, we're in the genesis transaction which mints the IOTA supply, and hence
960        // does not satisfy IOTA conservation, or we're in the non-production
961        // dev inspect mode which allows us to violate conservation
962        result
963    }
964
965    /// Runs checks on the input objects of a transaction to ensure that they
966    /// meet the necessary conditions for execution.
967    ///
968    /// It checks for denied certificates, deleted input objects, and cancelled
969    /// objects due to congestion or randomness unavailability. If any of
970    /// these conditions are met, it returns an appropriate
971    /// `ExecutionError`.
972    ///
973    /// If all checks pass, it returns `Ok(())`, indicating that the transaction
974    /// can proceed with execution.
975    #[instrument(name = "run_inputs_checks", level = "debug", skip_all)]
976    fn run_inputs_checks(
977        protocol_config: &ProtocolConfig,
978        deny_cert: bool,
979        contains_deleted_input: bool,
980        cancelled_objects: Option<(Vec<ObjectID>, SequenceNumber)>,
981    ) -> Result<(), ExecutionError> {
982        if deny_cert {
983            Err(ExecutionError::new(
984                ExecutionErrorKind::CertificateDenied,
985                None,
986            ))
987        } else if contains_deleted_input {
988            Err(ExecutionError::new(
989                ExecutionErrorKind::InputObjectDeleted,
990                None,
991            ))
992        } else if let Some((cancelled_objects, reason)) = cancelled_objects {
993            match reason {
994                version if version.is_congested() => Err(ExecutionError::new(
995                    if protocol_config.congestion_control_gas_price_feedback_mechanism() {
996                        ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestionV2 {
997                            congested_objects: CongestedObjects(cancelled_objects),
998                            suggested_gas_price: version
999                                .get_congested_version_suggested_gas_price(),
1000                        }
1001                    } else {
1002                        // WARN: do not remove this `else` branch even after
1003                        // `congestion_control_gas_price_feedback_mechanism` is enabled
1004                        // on the mainnet. It must be kept to be able to replay old
1005                        // transaction data.
1006                        ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
1007                            congested_objects: CongestedObjects(cancelled_objects),
1008                        }
1009                    },
1010                    None,
1011                )),
1012                SequenceNumber::RANDOMNESS_UNAVAILABLE => Err(ExecutionError::new(
1013                    ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable,
1014                    None,
1015                )),
1016                _ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
1017            }
1018        } else {
1019            Ok(())
1020        }
1021    }
1022
1023    /// Checks if the estimated size of transaction effects exceeds predefined
1024    /// limits based on the protocol configuration. For metered
1025    /// transactions, it enforces hard limits, while for system transactions, it
1026    /// allows soft limits with warnings.
1027    #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
1028    fn check_meter_limit(
1029        temporary_store: &mut TemporaryStore<'_>,
1030        gas_charger: &mut GasCharger,
1031        protocol_config: &ProtocolConfig,
1032        metrics: Arc<LimitsMetrics>,
1033    ) -> Result<(), ExecutionError> {
1034        let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
1035
1036        // Check if a limit threshold was crossed.
1037        // For metered transactions, there is not soft limit.
1038        // For system transactions, we allow a soft limit with alerting, and a hard
1039        // limit where we terminate
1040        match check_limit_by_meter!(
1041            !gas_charger.is_unmetered(),
1042            effects_estimated_size,
1043            protocol_config.max_serialized_tx_effects_size_bytes(),
1044            protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
1045            metrics.excessive_estimated_effects_size
1046        ) {
1047            LimitThresholdCrossed::None => Ok(()),
1048            LimitThresholdCrossed::Soft(_, limit) => {
1049                warn!(
1050                    effects_estimated_size = effects_estimated_size,
1051                    soft_limit = limit,
1052                    "Estimated transaction effects size crossed soft limit",
1053                );
1054                Ok(())
1055            }
1056            LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
1057                ExecutionErrorKind::EffectsTooLarge {
1058                    current_size: effects_estimated_size as u64,
1059                    max_size: lim as u64,
1060                },
1061                "Transaction effects are too large",
1062            )),
1063        }
1064    }
1065
1066    /// Checks if the total size of written objects in the transaction exceeds
1067    /// the limits defined in the protocol configuration. For metered
1068    /// transactions, it enforces a hard limit, while for system transactions,
1069    /// it allows a soft limit with warnings.
1070    #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
1071    fn check_written_objects_limit(
1072        temporary_store: &mut TemporaryStore<'_>,
1073        gas_charger: &mut GasCharger,
1074        protocol_config: &ProtocolConfig,
1075        metrics: Arc<LimitsMetrics>,
1076    ) -> Result<(), ExecutionError> {
1077        if let (Some(normal_lim), Some(system_lim)) = (
1078            protocol_config.max_size_written_objects_as_option(),
1079            protocol_config.max_size_written_objects_system_tx_as_option(),
1080        ) {
1081            let written_objects_size = temporary_store.written_objects_size();
1082
1083            match check_limit_by_meter!(
1084                !gas_charger.is_unmetered(),
1085                written_objects_size,
1086                normal_lim,
1087                system_lim,
1088                metrics.excessive_written_objects_size
1089            ) {
1090                LimitThresholdCrossed::None => (),
1091                LimitThresholdCrossed::Soft(_, limit) => {
1092                    warn!(
1093                        written_objects_size = written_objects_size,
1094                        soft_limit = limit,
1095                        "Written objects size crossed soft limit",
1096                    )
1097                }
1098                LimitThresholdCrossed::Hard(_, lim) => {
1099                    return Err(ExecutionError::new_with_source(
1100                        ExecutionErrorKind::WrittenObjectsTooLarge {
1101                            current_size: written_objects_size as u64,
1102                            max_size: lim as u64,
1103                        },
1104                        "Written objects size crossed hard limit",
1105                    ));
1106                }
1107            };
1108        }
1109
1110        Ok(())
1111    }
1112
1113    /// Executes the given transaction based on its `TransactionKind` by
1114    /// processing it through corresponding handlers such as epoch changes,
1115    /// genesis transactions, consensus commit prologues, and programmable
1116    /// transactions. For each type of transaction, the corresponding logic is
1117    /// invoked, such as advancing the epoch, setting up consensus commits, or
1118    /// executing a programmable transaction.
1119    #[instrument(level = "debug", skip_all)]
1120    fn execution_loop<Mode: ExecutionMode>(
1121        temporary_store: &mut TemporaryStore<'_>,
1122        transaction_kind: TransactionKind,
1123        tx_ctx: &mut TxContext,
1124        move_vm: &Arc<MoveVM>,
1125        gas_charger: &mut GasCharger,
1126        protocol_config: &ProtocolConfig,
1127        metrics: Arc<LimitsMetrics>,
1128        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1129    ) -> Result<Mode::ExecutionResults, ExecutionError> {
1130        let result = match transaction_kind {
1131            TransactionKind::Genesis(GenesisTransaction { objects, events }) => {
1132                if tx_ctx.epoch() != 0 {
1133                    panic!("BUG: Genesis Transactions can only be executed in epoch 0");
1134                }
1135
1136                for genesis_object in objects {
1137                    match genesis_object {
1138                        iota_types::transaction::GenesisObject::RawObject { data, owner } => {
1139                            let object = ObjectInner {
1140                                data,
1141                                owner,
1142                                previous_transaction: tx_ctx.digest(),
1143                                storage_rebate: 0,
1144                            };
1145                            temporary_store.create_object(object.into());
1146                        }
1147                    }
1148                }
1149
1150                temporary_store.record_execution_results(ExecutionResults::V1(
1151                    ExecutionResultsV1 {
1152                        user_events: events,
1153                        ..Default::default()
1154                    },
1155                ));
1156
1157                Ok(Mode::empty_results())
1158            }
1159            TransactionKind::ConsensusCommitPrologueV1(prologue) => {
1160                setup_consensus_commit(
1161                    prologue.commit_timestamp_ms,
1162                    temporary_store,
1163                    tx_ctx,
1164                    move_vm,
1165                    gas_charger,
1166                    protocol_config,
1167                    metrics,
1168                    trace_builder_opt,
1169                )
1170                .expect("ConsensusCommitPrologueV1 cannot fail");
1171                Ok(Mode::empty_results())
1172            }
1173            TransactionKind::ProgrammableTransaction(pt) => {
1174                programmable_transactions::execution::execute::<Mode>(
1175                    protocol_config,
1176                    metrics,
1177                    move_vm,
1178                    temporary_store,
1179                    tx_ctx,
1180                    gas_charger,
1181                    pt,
1182                    trace_builder_opt,
1183                )
1184            }
1185            TransactionKind::EndOfEpochTransaction(txns) => {
1186                let mut builder = ProgrammableTransactionBuilder::new();
1187                let len = txns.len();
1188                for (i, tx) in txns.into_iter().enumerate() {
1189                    match tx {
1190                        EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
1191                            assert_eq!(i, len - 1);
1192                            advance_epoch_v1(
1193                                builder,
1194                                change_epoch,
1195                                temporary_store,
1196                                tx_ctx,
1197                                move_vm,
1198                                gas_charger,
1199                                protocol_config,
1200                                metrics,
1201                                trace_builder_opt,
1202                            )?;
1203                            return Ok(Mode::empty_results());
1204                        }
1205                        EndOfEpochTransactionKind::ChangeEpochV2(change_epoch_v2) => {
1206                            assert_eq!(i, len - 1);
1207                            advance_epoch_v2(
1208                                builder,
1209                                change_epoch_v2,
1210                                temporary_store,
1211                                tx_ctx,
1212                                move_vm,
1213                                gas_charger,
1214                                protocol_config,
1215                                metrics,
1216                                trace_builder_opt,
1217                            )?;
1218                            return Ok(Mode::empty_results());
1219                        }
1220                        EndOfEpochTransactionKind::ChangeEpochV3(change_epoch_v3) => {
1221                            assert_eq!(i, len - 1);
1222                            advance_epoch_v3(
1223                                builder,
1224                                change_epoch_v3,
1225                                temporary_store,
1226                                tx_ctx,
1227                                move_vm,
1228                                gas_charger,
1229                                protocol_config,
1230                                metrics,
1231                                trace_builder_opt,
1232                            )?;
1233                            return Ok(Mode::empty_results());
1234                        }
1235                        EndOfEpochTransactionKind::ChangeEpochV4(change_epoch_v4) => {
1236                            assert_eq!(i, len - 1);
1237                            advance_epoch_v4(
1238                                builder,
1239                                change_epoch_v4,
1240                                temporary_store,
1241                                tx_ctx,
1242                                move_vm,
1243                                gas_charger,
1244                                protocol_config,
1245                                metrics,
1246                                trace_builder_opt,
1247                            )?;
1248                            return Ok(Mode::empty_results());
1249                        }
1250                        EndOfEpochTransactionKind::AuthenticatorStateCreate => {
1251                            assert!(protocol_config.enable_jwk_consensus_updates());
1252                            builder = setup_authenticator_state_create(builder);
1253                        }
1254                        EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
1255                            assert!(protocol_config.enable_jwk_consensus_updates());
1256
1257                            // TODO: it would be nice if a failure of this function didn't cause
1258                            // safe mode.
1259                            builder = setup_authenticator_state_expire(builder, expire);
1260                        }
1261                    }
1262                }
1263                unreachable!(
1264                    "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
1265                )
1266            }
1267            TransactionKind::AuthenticatorStateUpdateV1(auth_state_update) => {
1268                setup_authenticator_state_update(
1269                    auth_state_update,
1270                    temporary_store,
1271                    tx_ctx,
1272                    move_vm,
1273                    gas_charger,
1274                    protocol_config,
1275                    metrics,
1276                    trace_builder_opt,
1277                )?;
1278                Ok(Mode::empty_results())
1279            }
1280            TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
1281                setup_randomness_state_update(
1282                    randomness_state_update,
1283                    temporary_store,
1284                    tx_ctx,
1285                    move_vm,
1286                    gas_charger,
1287                    protocol_config,
1288                    metrics,
1289                    trace_builder_opt,
1290                )?;
1291                Ok(Mode::empty_results())
1292            }
1293        }?;
1294        temporary_store.check_execution_results_consistency()?;
1295        Ok(result)
1296    }
1297
1298    /// Mints epoch rewards by creating both storage and computation charges
1299    /// using a `ProgrammableTransactionBuilder`. The function takes in the
1300    /// `AdvanceEpochParams`, serializes the storage and computation
1301    /// charges, and invokes the reward creation function within the IOTA
1302    /// Prepares invocations for creating both storage and computation charges
1303    /// with a `ProgrammableTransactionBuilder` using the `AdvanceEpochParams`.
1304    /// The corresponding functions from the IOTA framework can be invoked later
1305    /// during execution of the programmable transaction.
1306    fn mint_epoch_rewards_in_pt(
1307        builder: &mut ProgrammableTransactionBuilder,
1308        params: &AdvanceEpochParams,
1309    ) -> (Argument, Argument) {
1310        // Create storage charges.
1311        let storage_charge_arg = builder
1312            .input(CallArg::Pure(
1313                bcs::to_bytes(&params.storage_charge).unwrap(),
1314            ))
1315            .unwrap();
1316        let storage_charges = builder.programmable_move_call(
1317            IOTA_FRAMEWORK_PACKAGE_ID,
1318            BALANCE_MODULE_NAME.to_owned(),
1319            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
1320            vec![GAS::type_tag()],
1321            vec![storage_charge_arg],
1322        );
1323
1324        // Create computation charges.
1325        let computation_charge_arg = builder
1326            .input(CallArg::Pure(
1327                bcs::to_bytes(&params.computation_charge).unwrap(),
1328            ))
1329            .unwrap();
1330        let computation_charges = builder.programmable_move_call(
1331            IOTA_FRAMEWORK_PACKAGE_ID,
1332            BALANCE_MODULE_NAME.to_owned(),
1333            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
1334            vec![GAS::type_tag()],
1335            vec![computation_charge_arg],
1336        );
1337        (storage_charges, computation_charges)
1338    }
1339
1340    /// Constructs a `ProgrammableTransaction` to advance the epoch. It creates
1341    /// storage charges and computation charges by invoking
1342    /// `mint_epoch_rewards_in_pt`, advances the epoch by setting up the
1343    /// necessary arguments, such as epoch number, protocol version, storage
1344    /// rebate, and slashing rate, and executing the `advance_epoch` function
1345    /// within the IOTA system. Then, it destroys the storage rebates to
1346    /// complete the transaction.
1347    pub fn construct_advance_epoch_pt_impl(
1348        mut builder: ProgrammableTransactionBuilder,
1349        params: &AdvanceEpochParams,
1350        call_arg_vec: Vec<CallArg>,
1351    ) -> Result<ProgrammableTransaction, ExecutionError> {
1352        // Create storage and computation charges and add them as arguments.
1353        let (storage_charges, computation_charges) = mint_epoch_rewards_in_pt(&mut builder, params);
1354        let mut arguments = vec![
1355            builder
1356                .pure(params.validator_subsidy)
1357                .expect("bcs encoding a u64 should not fail"),
1358            storage_charges,
1359            computation_charges,
1360        ];
1361
1362        let call_arg_arguments = call_arg_vec
1363            .into_iter()
1364            .map(|a| builder.input(a))
1365            .collect::<Result<_, _>>();
1366
1367        assert_invariant!(
1368            call_arg_arguments.is_ok(),
1369            "Unable to generate args for advance_epoch transaction!"
1370        );
1371
1372        arguments.append(&mut call_arg_arguments.unwrap());
1373
1374        info!("Call arguments to advance_epoch transaction: {:?}", params);
1375
1376        let storage_rebates = builder.programmable_move_call(
1377            IOTA_SYSTEM_PACKAGE_ID,
1378            IOTA_SYSTEM_MODULE_NAME.to_owned(),
1379            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
1380            vec![],
1381            arguments,
1382        );
1383
1384        // Step 3: Destroy the storage rebates.
1385        builder.programmable_move_call(
1386            IOTA_FRAMEWORK_PACKAGE_ID,
1387            BALANCE_MODULE_NAME.to_owned(),
1388            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
1389            vec![GAS::type_tag()],
1390            vec![storage_rebates],
1391        );
1392        Ok(builder.finish())
1393    }
1394
1395    pub fn construct_advance_epoch_pt_v1(
1396        builder: ProgrammableTransactionBuilder,
1397        params: &AdvanceEpochParams,
1398    ) -> Result<ProgrammableTransaction, ExecutionError> {
1399        // the first three arguments to the advance_epoch function, namely
1400        // validator_subsidy, storage_charges and computation_charges, are
1401        // common to both v1 and v2 and are added in `construct_advance_epoch_pt_impl`.
1402        // The remaining arguments are added here.
1403        let call_arg_vec = vec![
1404            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
1405            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
1406            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
1407            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
1408            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
1409            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
1410            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
1411        ];
1412        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
1413    }
1414
1415    pub fn construct_advance_epoch_pt_v2(
1416        builder: ProgrammableTransactionBuilder,
1417        params: &AdvanceEpochParams,
1418    ) -> Result<ProgrammableTransaction, ExecutionError> {
1419        // the first three arguments to the advance_epoch function, namely
1420        // validator_subsidy, storage_charges and computation_charges, are
1421        // common to both v1 and v2 and are added in `construct_advance_epoch_pt_impl`.
1422        // The remaining arguments are added here.
1423        let call_arg_vec = vec![
1424            CallArg::Pure(bcs::to_bytes(&params.computation_charge_burned).unwrap()), /* computation_charge_burned: u64 */
1425            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
1426            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
1427            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
1428            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
1429            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
1430            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
1431            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
1432            CallArg::Pure(bcs::to_bytes(&params.max_committee_members_count).unwrap()), /* max_committee_members_count: u64 */
1433        ];
1434        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
1435    }
1436
1437    pub fn construct_advance_epoch_pt_v3(
1438        builder: ProgrammableTransactionBuilder,
1439        params: &AdvanceEpochParams,
1440    ) -> Result<ProgrammableTransaction, ExecutionError> {
1441        // the first three arguments to the advance_epoch function, namely
1442        // validator_subsidy, storage_charges and computation_charges, are
1443        // common to both v1, v2 and v3 and are added in
1444        // `construct_advance_epoch_pt_impl`. The remaining arguments are added
1445        // here.
1446        let call_arg_vec = vec![
1447            CallArg::Pure(bcs::to_bytes(&params.computation_charge_burned).unwrap()), /* computation_charge_burned: u64 */
1448            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
1449            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
1450            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
1451            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
1452            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
1453            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
1454            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
1455            CallArg::Pure(bcs::to_bytes(&params.max_committee_members_count).unwrap()), /* max_committee_members_count: u64 */
1456            CallArg::Pure(bcs::to_bytes(&params.eligible_active_validators).unwrap()), /* eligible_active_validators: Vec<u64> */
1457        ];
1458        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
1459    }
1460
1461    pub fn construct_advance_epoch_pt_v4(
1462        builder: ProgrammableTransactionBuilder,
1463        params: &AdvanceEpochParams,
1464    ) -> Result<ProgrammableTransaction, ExecutionError> {
1465        // the first three arguments to the advance_epoch function, namely
1466        // validator_subsidy, storage_charges and computation_charges, are
1467        // common to both v1, v2, v3 and v4 and are added in
1468        // `construct_advance_epoch_pt_impl`. The remaining arguments are added
1469        // here.
1470        let call_arg_vec = vec![
1471            CallArg::Pure(bcs::to_bytes(&params.computation_charge_burned).unwrap()), /* computation_charge_burned: u64 */
1472            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
1473            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
1474            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
1475            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
1476            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
1477            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
1478            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
1479            CallArg::Pure(bcs::to_bytes(&params.max_committee_members_count).unwrap()), /* max_committee_members_count: u64 */
1480            CallArg::Pure(bcs::to_bytes(&params.eligible_active_validators).unwrap()), /* eligible_active_validators: Vec<u64> */
1481            CallArg::Pure(bcs::to_bytes(&params.scores).unwrap()), // scores: Vec<u64>
1482            CallArg::Pure(bcs::to_bytes(&params.adjust_rewards_by_score).unwrap()), /* adjust_rewards_by_score: bool */
1483        ];
1484        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
1485    }
1486
1487    /// Advances the epoch by executing a `ProgrammableTransaction`. If the
1488    /// transaction fails, it switches to safe mode and retries the epoch
1489    /// advancement in a more controlled environment. The function also
1490    /// handles the publication and upgrade of system packages for the new
1491    /// epoch. If any system package is added or upgraded, it ensures the
1492    /// proper execution and storage of the changes.
1493    fn advance_epoch_impl(
1494        advance_epoch_pt: ProgrammableTransaction,
1495        params: AdvanceEpochParams,
1496        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
1497        temporary_store: &mut TemporaryStore<'_>,
1498        tx_ctx: &mut TxContext,
1499        move_vm: &Arc<MoveVM>,
1500        gas_charger: &mut GasCharger,
1501        protocol_config: &ProtocolConfig,
1502        metrics: Arc<LimitsMetrics>,
1503        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1504    ) -> Result<(), ExecutionError> {
1505        let result = programmable_transactions::execution::execute::<execution_mode::System>(
1506            protocol_config,
1507            metrics.clone(),
1508            move_vm,
1509            temporary_store,
1510            tx_ctx,
1511            gas_charger,
1512            advance_epoch_pt,
1513            trace_builder_opt,
1514        );
1515
1516        #[cfg(msim)]
1517        let result = maybe_modify_result(result, params.epoch);
1518
1519        if result.is_err() {
1520            tracing::error!(
1521                "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx params: {:?}",
1522                result.as_ref().err(),
1523                temporary_store.objects(),
1524                params,
1525            );
1526            temporary_store.drop_writes();
1527            // Must reset the storage rebate since we are re-executing.
1528            gas_charger.reset_storage_cost_and_rebate();
1529
1530            temporary_store.advance_epoch_safe_mode(&params, protocol_config);
1531        }
1532
1533        let new_vm = new_move_vm(
1534            all_natives(/* silent */ true, protocol_config),
1535            protocol_config,
1536            // enable_profiler
1537            None,
1538        )
1539        .expect("Failed to create new MoveVM");
1540        process_system_packages(
1541            system_packages,
1542            temporary_store,
1543            tx_ctx,
1544            &new_vm,
1545            gas_charger,
1546            protocol_config,
1547            metrics,
1548            trace_builder_opt,
1549        );
1550
1551        Ok(())
1552    }
1553
1554    /// Advances the epoch for the given `ChangeEpoch` transaction kind by
1555    /// constructing a programmable transaction, executing it and processing the
1556    /// system packages.
1557    fn advance_epoch_v1(
1558        builder: ProgrammableTransactionBuilder,
1559        change_epoch: ChangeEpoch,
1560        temporary_store: &mut TemporaryStore<'_>,
1561        tx_ctx: &mut TxContext,
1562        move_vm: &Arc<MoveVM>,
1563        gas_charger: &mut GasCharger,
1564        protocol_config: &ProtocolConfig,
1565        metrics: Arc<LimitsMetrics>,
1566        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1567    ) -> Result<(), ExecutionError> {
1568        let params = AdvanceEpochParams {
1569            epoch: change_epoch.epoch,
1570            next_protocol_version: change_epoch.protocol_version,
1571            validator_subsidy: protocol_config.validator_target_reward(),
1572            storage_charge: change_epoch.storage_charge,
1573            computation_charge: change_epoch.computation_charge,
1574            // all computation charge is burned in v1
1575            computation_charge_burned: change_epoch.computation_charge,
1576            storage_rebate: change_epoch.storage_rebate,
1577            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
1578            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1579            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
1580            // AdvanceEpochV1 does not use those fields, but keeping them to avoid creating a
1581            // separate AdvanceEpochParams struct.
1582            max_committee_members_count: 0,
1583            eligible_active_validators: vec![],
1584            scores: vec![],
1585            adjust_rewards_by_score: false,
1586        };
1587        let advance_epoch_pt = construct_advance_epoch_pt_v1(builder, &params)?;
1588        advance_epoch_impl(
1589            advance_epoch_pt,
1590            params,
1591            change_epoch.system_packages,
1592            temporary_store,
1593            tx_ctx,
1594            move_vm,
1595            gas_charger,
1596            protocol_config,
1597            metrics,
1598            trace_builder_opt,
1599        )
1600    }
1601
1602    /// Advances the epoch for the given `ChangeEpochV2` transaction kind by
1603    /// constructing a programmable transaction, executing it and processing the
1604    /// system packages.
1605    fn advance_epoch_v2(
1606        builder: ProgrammableTransactionBuilder,
1607        change_epoch_v2: ChangeEpochV2,
1608        temporary_store: &mut TemporaryStore<'_>,
1609        tx_ctx: &mut TxContext,
1610        move_vm: &Arc<MoveVM>,
1611        gas_charger: &mut GasCharger,
1612        protocol_config: &ProtocolConfig,
1613        metrics: Arc<LimitsMetrics>,
1614        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1615    ) -> Result<(), ExecutionError> {
1616        let params = AdvanceEpochParams {
1617            epoch: change_epoch_v2.epoch,
1618            next_protocol_version: change_epoch_v2.protocol_version,
1619            validator_subsidy: protocol_config.validator_target_reward(),
1620            storage_charge: change_epoch_v2.storage_charge,
1621            computation_charge: change_epoch_v2.computation_charge,
1622            computation_charge_burned: change_epoch_v2.computation_charge_burned,
1623            storage_rebate: change_epoch_v2.storage_rebate,
1624            non_refundable_storage_fee: change_epoch_v2.non_refundable_storage_fee,
1625            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1626            epoch_start_timestamp_ms: change_epoch_v2.epoch_start_timestamp_ms,
1627            max_committee_members_count: protocol_config.max_committee_members_count(),
1628            // AdvanceEpochV2 does not use these fields, but keeping them to avoid creating a
1629            // separate AdvanceEpochParams struct.
1630            eligible_active_validators: vec![],
1631            scores: vec![],
1632            adjust_rewards_by_score: false,
1633        };
1634        let advance_epoch_pt = construct_advance_epoch_pt_v2(builder, &params)?;
1635        advance_epoch_impl(
1636            advance_epoch_pt,
1637            params,
1638            change_epoch_v2.system_packages,
1639            temporary_store,
1640            tx_ctx,
1641            move_vm,
1642            gas_charger,
1643            protocol_config,
1644            metrics,
1645            trace_builder_opt,
1646        )
1647    }
1648
1649    /// Advances the epoch for the given `ChangeEpochV3` transaction kind by
1650    /// constructing a programmable transaction, executing it and processing the
1651    /// system packages.
1652    fn advance_epoch_v3(
1653        builder: ProgrammableTransactionBuilder,
1654        change_epoch_v3: ChangeEpochV3,
1655        temporary_store: &mut TemporaryStore<'_>,
1656        tx_ctx: &mut TxContext,
1657        move_vm: &Arc<MoveVM>,
1658        gas_charger: &mut GasCharger,
1659        protocol_config: &ProtocolConfig,
1660        metrics: Arc<LimitsMetrics>,
1661        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1662    ) -> Result<(), ExecutionError> {
1663        let params = AdvanceEpochParams {
1664            epoch: change_epoch_v3.epoch,
1665            next_protocol_version: change_epoch_v3.protocol_version,
1666            validator_subsidy: protocol_config.validator_target_reward(),
1667            storage_charge: change_epoch_v3.storage_charge,
1668            computation_charge: change_epoch_v3.computation_charge,
1669            computation_charge_burned: change_epoch_v3.computation_charge_burned,
1670            storage_rebate: change_epoch_v3.storage_rebate,
1671            non_refundable_storage_fee: change_epoch_v3.non_refundable_storage_fee,
1672            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1673            epoch_start_timestamp_ms: change_epoch_v3.epoch_start_timestamp_ms,
1674            max_committee_members_count: protocol_config.max_committee_members_count(),
1675            eligible_active_validators: change_epoch_v3.eligible_active_validators,
1676            // AdvanceEpochV3 does not use these fields, but keeping them to avoid creating a
1677            // separate AdvanceEpochParams struct.
1678            scores: vec![],
1679            adjust_rewards_by_score: false,
1680        };
1681        let advance_epoch_pt = construct_advance_epoch_pt_v3(builder, &params)?;
1682        advance_epoch_impl(
1683            advance_epoch_pt,
1684            params,
1685            change_epoch_v3.system_packages,
1686            temporary_store,
1687            tx_ctx,
1688            move_vm,
1689            gas_charger,
1690            protocol_config,
1691            metrics,
1692            trace_builder_opt,
1693        )
1694    }
1695
1696    /// Advances the epoch for the given `ChangeEpochV4` transaction kind by
1697    /// constructing a programmable transaction, executing it and processing the
1698    /// system packages.
1699    fn advance_epoch_v4(
1700        builder: ProgrammableTransactionBuilder,
1701        change_epoch_v4: ChangeEpochV4,
1702        temporary_store: &mut TemporaryStore<'_>,
1703        tx_ctx: &mut TxContext,
1704        move_vm: &Arc<MoveVM>,
1705        gas_charger: &mut GasCharger,
1706        protocol_config: &ProtocolConfig,
1707        metrics: Arc<LimitsMetrics>,
1708        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1709    ) -> Result<(), ExecutionError> {
1710        let params = AdvanceEpochParams {
1711            epoch: change_epoch_v4.epoch,
1712            next_protocol_version: change_epoch_v4.protocol_version,
1713            validator_subsidy: protocol_config.validator_target_reward(),
1714            storage_charge: change_epoch_v4.storage_charge,
1715            computation_charge: change_epoch_v4.computation_charge,
1716            computation_charge_burned: change_epoch_v4.computation_charge_burned,
1717            storage_rebate: change_epoch_v4.storage_rebate,
1718            non_refundable_storage_fee: change_epoch_v4.non_refundable_storage_fee,
1719            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1720            epoch_start_timestamp_ms: change_epoch_v4.epoch_start_timestamp_ms,
1721            max_committee_members_count: protocol_config.max_committee_members_count(),
1722            eligible_active_validators: change_epoch_v4.eligible_active_validators,
1723            scores: change_epoch_v4.scores,
1724            adjust_rewards_by_score: change_epoch_v4.adjust_rewards_by_score,
1725        };
1726        let advance_epoch_pt = construct_advance_epoch_pt_v4(builder, &params)?;
1727        advance_epoch_impl(
1728            advance_epoch_pt,
1729            params,
1730            change_epoch_v4.system_packages,
1731            temporary_store,
1732            tx_ctx,
1733            move_vm,
1734            gas_charger,
1735            protocol_config,
1736            metrics,
1737            trace_builder_opt,
1738        )
1739    }
1740
1741    fn process_system_packages(
1742        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
1743        temporary_store: &mut TemporaryStore<'_>,
1744        tx_ctx: &mut TxContext,
1745        move_vm: &MoveVM,
1746        gas_charger: &mut GasCharger,
1747        protocol_config: &ProtocolConfig,
1748        metrics: Arc<LimitsMetrics>,
1749        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1750    ) {
1751        let binary_config = to_binary_config(protocol_config);
1752        for (version, modules, dependencies) in system_packages.into_iter() {
1753            let deserialized_modules: Vec<_> = modules
1754                .iter()
1755                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
1756                .collect();
1757
1758            if version == OBJECT_START_VERSION {
1759                let package_id = deserialized_modules.first().unwrap().address();
1760                info!("adding new system package {package_id}");
1761
1762                let publish_pt = {
1763                    let mut b = ProgrammableTransactionBuilder::new();
1764                    b.command(Command::Publish(modules, dependencies));
1765                    b.finish()
1766                };
1767
1768                programmable_transactions::execution::execute::<execution_mode::System>(
1769                    protocol_config,
1770                    metrics.clone(),
1771                    move_vm,
1772                    temporary_store,
1773                    tx_ctx,
1774                    gas_charger,
1775                    publish_pt,
1776                    trace_builder_opt,
1777                )
1778                .expect("System Package Publish must succeed");
1779            } else {
1780                let mut new_package = Object::new_system_package(
1781                    &deserialized_modules,
1782                    version,
1783                    dependencies,
1784                    tx_ctx.digest(),
1785                );
1786
1787                info!(
1788                    "upgraded system package {:?}",
1789                    new_package.compute_object_reference()
1790                );
1791
1792                // Decrement the version before writing the package so that the store can record
1793                // the version growing by one in the effects.
1794                new_package
1795                    .data
1796                    .try_as_package_mut()
1797                    .unwrap()
1798                    .decrement_version();
1799
1800                // upgrade of a previously existing framework module
1801                temporary_store.upgrade_system_package(new_package);
1802            }
1803        }
1804    }
1805
1806    /// Perform metadata updates in preparation for the transactions in the
1807    /// upcoming checkpoint:
1808    ///
1809    /// - Set the timestamp for the `Clock` shared object from the timestamp in
1810    ///   the header from consensus.
1811    fn setup_consensus_commit(
1812        consensus_commit_timestamp_ms: CheckpointTimestamp,
1813        temporary_store: &mut TemporaryStore<'_>,
1814        tx_ctx: &mut TxContext,
1815        move_vm: &Arc<MoveVM>,
1816        gas_charger: &mut GasCharger,
1817        protocol_config: &ProtocolConfig,
1818        metrics: Arc<LimitsMetrics>,
1819        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1820    ) -> Result<(), ExecutionError> {
1821        let pt = {
1822            let mut builder = ProgrammableTransactionBuilder::new();
1823            let res = builder.move_call(
1824                IOTA_FRAMEWORK_ADDRESS.into(),
1825                CLOCK_MODULE_NAME.to_owned(),
1826                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
1827                vec![],
1828                vec![
1829                    CallArg::CLOCK_MUT,
1830                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
1831                ],
1832            );
1833            assert_invariant!(
1834                res.is_ok(),
1835                "Unable to generate consensus_commit_prologue transaction!"
1836            );
1837            builder.finish()
1838        };
1839        programmable_transactions::execution::execute::<execution_mode::System>(
1840            protocol_config,
1841            metrics,
1842            move_vm,
1843            temporary_store,
1844            tx_ctx,
1845            gas_charger,
1846            pt,
1847            trace_builder_opt,
1848        )
1849    }
1850
1851    /// This function adds a Move call to the IOTA framework's
1852    /// `authenticator_state_create` function, preparing the transaction for
1853    /// execution.
1854    fn setup_authenticator_state_create(
1855        mut builder: ProgrammableTransactionBuilder,
1856    ) -> ProgrammableTransactionBuilder {
1857        builder
1858            .move_call(
1859                IOTA_FRAMEWORK_ADDRESS.into(),
1860                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1861                AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1862                vec![],
1863                vec![],
1864            )
1865            .expect("Unable to generate authenticator_state_create transaction!");
1866        builder
1867    }
1868
1869    /// Sets up and executes a `ProgrammableTransaction` to update the
1870    /// authenticator state. This function constructs a transaction that
1871    /// invokes the `authenticator_state_update` function from the IOTA
1872    /// framework, passing the authenticator state object and new active JWKS as
1873    /// arguments. It then executes the transaction using the system
1874    /// execution mode.
1875    fn setup_authenticator_state_update(
1876        update: AuthenticatorStateUpdateV1,
1877        temporary_store: &mut TemporaryStore<'_>,
1878        tx_ctx: &mut TxContext,
1879        move_vm: &Arc<MoveVM>,
1880        gas_charger: &mut GasCharger,
1881        protocol_config: &ProtocolConfig,
1882        metrics: Arc<LimitsMetrics>,
1883        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1884    ) -> Result<(), ExecutionError> {
1885        let pt = {
1886            let mut builder = ProgrammableTransactionBuilder::new();
1887            let res = builder.move_call(
1888                IOTA_FRAMEWORK_ADDRESS.into(),
1889                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1890                AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1891                vec![],
1892                vec![
1893                    CallArg::Object(ObjectArg::SharedObject {
1894                        id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1895                        initial_shared_version: update.authenticator_obj_initial_shared_version,
1896                        mutable: true,
1897                    }),
1898                    CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1899                ],
1900            );
1901            assert_invariant!(
1902                res.is_ok(),
1903                "Unable to generate authenticator_state_update transaction!"
1904            );
1905            builder.finish()
1906        };
1907        programmable_transactions::execution::execute::<execution_mode::System>(
1908            protocol_config,
1909            metrics,
1910            move_vm,
1911            temporary_store,
1912            tx_ctx,
1913            gas_charger,
1914            pt,
1915            trace_builder_opt,
1916        )
1917    }
1918
1919    /// Configures a `ProgrammableTransactionBuilder` to expire authenticator
1920    /// state by invoking the `authenticator_state_expire_jwks` function
1921    /// from the IOTA framework. The function adds the necessary Move call
1922    /// with the authenticator state object and the minimum epoch as arguments.
1923    fn setup_authenticator_state_expire(
1924        mut builder: ProgrammableTransactionBuilder,
1925        expire: AuthenticatorStateExpire,
1926    ) -> ProgrammableTransactionBuilder {
1927        builder
1928            .move_call(
1929                IOTA_FRAMEWORK_ADDRESS.into(),
1930                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1931                AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1932                vec![],
1933                vec![
1934                    CallArg::Object(ObjectArg::SharedObject {
1935                        id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1936                        initial_shared_version: expire.authenticator_obj_initial_shared_version,
1937                        mutable: true,
1938                    }),
1939                    CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1940                ],
1941            )
1942            .expect("Unable to generate authenticator_state_expire transaction!");
1943        builder
1944    }
1945
1946    /// The function constructs a transaction that invokes
1947    /// the `randomness_state_update` function from the IOTA framework,
1948    /// passing the randomness state object, the `randomness_round`,
1949    /// and the `random_bytes` as arguments. It then executes the transaction
1950    /// using the system execution mode.
1951    fn setup_randomness_state_update(
1952        update: RandomnessStateUpdate,
1953        temporary_store: &mut TemporaryStore<'_>,
1954        tx_ctx: &mut TxContext,
1955        move_vm: &Arc<MoveVM>,
1956        gas_charger: &mut GasCharger,
1957        protocol_config: &ProtocolConfig,
1958        metrics: Arc<LimitsMetrics>,
1959        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1960    ) -> Result<(), ExecutionError> {
1961        let pt = {
1962            let mut builder = ProgrammableTransactionBuilder::new();
1963            let res = builder.move_call(
1964                IOTA_FRAMEWORK_ADDRESS.into(),
1965                RANDOMNESS_MODULE_NAME.to_owned(),
1966                RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1967                vec![],
1968                vec![
1969                    CallArg::Object(ObjectArg::SharedObject {
1970                        id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1971                        initial_shared_version: update.randomness_obj_initial_shared_version,
1972                        mutable: true,
1973                    }),
1974                    CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1975                    CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1976                ],
1977            );
1978            assert_invariant!(
1979                res.is_ok(),
1980                "Unable to generate randomness_state_update transaction!"
1981            );
1982            builder.finish()
1983        };
1984        programmable_transactions::execution::execute::<execution_mode::System>(
1985            protocol_config,
1986            metrics,
1987            move_vm,
1988            temporary_store,
1989            tx_ctx,
1990            gas_charger,
1991            pt,
1992            trace_builder_opt,
1993        )
1994    }
1995
1996    /// Construct a PTB with a single move call. This calls the authenticator
1997    /// function found in `AuthenticatorFunctionRef`. The inputs for the
1998    /// function are found in `MoveAuthenticator`.
1999    /// `MoveAuthenticator::object_to_authenticate` is added as the first
2000    /// argument to the created PTB, followed by all arguments in
2001    /// `MoveAuthenticator::call_args`.
2002    fn setup_authenticator_move_call(
2003        authenticator: MoveAuthenticator,
2004        authenticator_function_ref: AuthenticatorFunctionRefV1,
2005    ) -> Result<ProgrammableTransaction, ExecutionError> {
2006        let mut builder = ProgrammableTransactionBuilder::new();
2007
2008        let mut args = vec![authenticator.object_to_authenticate().to_owned()];
2009        args.extend(authenticator.call_args().to_owned());
2010
2011        let type_arguments = authenticator
2012            .type_arguments()
2013            .iter()
2014            .map(|t| {
2015                t.as_type_tag().map_err(|err| {
2016                    ExecutionError::new_with_source(
2017                        ExecutionErrorKind::VMInvariantViolation,
2018                        err.to_string(),
2019                    )
2020                })
2021            })
2022            .collect::<Result<Vec<_>, _>>()?;
2023
2024        let res = builder.move_call(
2025            authenticator_function_ref.package,
2026            Identifier::new(authenticator_function_ref.module.clone()).expect(
2027                "`AuthenticatorFunctionRefV1::module` is expected to be a valid `Identifier`",
2028            ),
2029            Identifier::new(authenticator_function_ref.function.clone()).expect(
2030                "`AuthenticatorFunctionRefV1::function` is expected to be a valid `Identifier`",
2031            ),
2032            type_arguments,
2033            args,
2034        );
2035
2036        assert_invariant!(
2037            res.is_ok(),
2038            "Unable to generate an account authenticator call transaction!"
2039        );
2040
2041        Ok(builder.finish())
2042    }
2043}