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