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