Skip to main content

iota_adapter_latest/
execution_engine.rs

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