Skip to main content

iota_adapter_latest/
execution_engine.rs

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