Skip to main content

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