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