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