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