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