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