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