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