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