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