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::{collections::HashSet, sync::Arc};
11
12    use iota_move_natives::all_natives;
13    use iota_protocol_config::{LimitThresholdCrossed, ProtocolConfig, check_limit_by_meter};
14    #[cfg(msim)]
15    use iota_types::iota_system_state::advance_epoch_result_injection::maybe_modify_result;
16    use iota_types::{
17        IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS, IOTA_FRAMEWORK_PACKAGE_ID,
18        IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID,
19        authenticator_state::{
20            AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME,
21            AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME, AUTHENTICATOR_STATE_MODULE_NAME,
22            AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
23        },
24        balance::{
25            BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
26            BALANCE_MODULE_NAME,
27        },
28        base_types::{
29            IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, TxContext,
30        },
31        clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME},
32        committee::EpochId,
33        effects::TransactionEffects,
34        error::{ExecutionError, ExecutionErrorKind},
35        execution::{ExecutionResults, ExecutionResultsV1, is_certificate_denied},
36        execution_config_utils::to_binary_config,
37        execution_status::{CongestedObjects, ExecutionStatus},
38        gas::{GasCostSummary, IotaGasStatus},
39        gas_coin::GAS,
40        inner_temporary_store::InnerTemporaryStore,
41        iota_system_state::{
42            ADVANCE_EPOCH_FUNCTION_NAME, AdvanceEpochParams, IOTA_SYSTEM_MODULE_NAME,
43        },
44        messages_checkpoint::CheckpointTimestamp,
45        metrics::LimitsMetrics,
46        object::{OBJECT_START_VERSION, Object, ObjectInner},
47        programmable_transaction_builder::ProgrammableTransactionBuilder,
48        randomness_state::{RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_UPDATE_FUNCTION_NAME},
49        storage::{BackingStore, Storage},
50        transaction::{
51            Argument, AuthenticatorStateExpire, AuthenticatorStateUpdateV1, CallArg, ChangeEpoch,
52            ChangeEpochV2, CheckedInputObjects, Command, EndOfEpochTransactionKind,
53            GenesisTransaction, ObjectArg, ProgrammableTransaction, RandomnessStateUpdate,
54            TransactionKind,
55        },
56    };
57    use move_binary_format::CompiledModule;
58    use move_trace_format::format::MoveTraceBuilder;
59    use move_vm_runtime::move_vm::MoveVM;
60    use tracing::{info, instrument, trace, warn};
61
62    use crate::{
63        adapter::new_move_vm,
64        execution_mode::{self, ExecutionMode},
65        gas_charger::GasCharger,
66        programmable_transactions,
67        temporary_store::TemporaryStore,
68        type_layout_resolver::TypeLayoutResolver,
69    };
70
71    /// The main entry point to the adapter's transaction execution. It
72    /// prepares a transaction for execution, then executes it through an
73    /// inner execution method and finally produces an instance of
74    /// transaction effects. It also returns the inner temporary store, which
75    /// contains the objects resulting from the transaction execution, the gas
76    /// status instance, which tracks the gas usage, and the execution result.
77    /// The function handles transaction execution based on the provided
78    /// `TransactionKind`. It checks for any expensive operations, manages
79    /// shared object references, and ensures transaction dependencies are
80    /// met. The returned objects are not committed to the store until the
81    /// resulting effects are applied by the caller.
82    #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
83    pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
84        store: &dyn BackingStore,
85        input_objects: CheckedInputObjects,
86        gas_coins: Vec<ObjectRef>,
87        gas_status: IotaGasStatus,
88        transaction_kind: TransactionKind,
89        transaction_signer: IotaAddress,
90        transaction_digest: TransactionDigest,
91        move_vm: &Arc<MoveVM>,
92        epoch_id: &EpochId,
93        epoch_timestamp_ms: u64,
94        protocol_config: &ProtocolConfig,
95        metrics: Arc<LimitsMetrics>,
96        enable_expensive_checks: bool,
97        certificate_deny_set: &HashSet<TransactionDigest>,
98        trace_builder_opt: &mut Option<MoveTraceBuilder>,
99    ) -> (
100        InnerTemporaryStore,
101        IotaGasStatus,
102        TransactionEffects,
103        Result<Mode::ExecutionResults, ExecutionError>,
104    ) {
105        let input_objects = input_objects.into_inner();
106        let mutable_inputs = if enable_expensive_checks {
107            input_objects.mutable_inputs().keys().copied().collect()
108        } else {
109            HashSet::new()
110        };
111        let shared_object_refs = input_objects.filter_shared_objects();
112        let receiving_objects = transaction_kind.receiving_objects();
113        let mut transaction_dependencies = input_objects.transaction_dependencies();
114        let contains_deleted_input = input_objects.contains_deleted_objects();
115        let cancelled_objects = input_objects.get_cancelled_objects();
116
117        let mut temporary_store = TemporaryStore::new(
118            store,
119            input_objects,
120            receiving_objects,
121            transaction_digest,
122            protocol_config,
123            *epoch_id,
124        );
125
126        let mut gas_charger =
127            GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
128
129        let mut tx_ctx = TxContext::new_from_components(
130            &transaction_signer,
131            &transaction_digest,
132            epoch_id,
133            epoch_timestamp_ms,
134        );
135
136        let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
137
138        let deny_cert = is_certificate_denied(&transaction_digest, certificate_deny_set);
139        let (gas_cost_summary, execution_result) = execute_transaction::<Mode>(
140            &mut temporary_store,
141            transaction_kind,
142            &mut gas_charger,
143            &mut tx_ctx,
144            move_vm,
145            protocol_config,
146            metrics,
147            enable_expensive_checks,
148            deny_cert,
149            contains_deleted_input,
150            cancelled_objects,
151            trace_builder_opt,
152        );
153
154        let status = if let Err(error) = &execution_result {
155            // Elaborate errors in logs if they are unexpected or their status is terse.
156            use ExecutionErrorKind as K;
157            match error.kind() {
158                K::InvariantViolation | K::VMInvariantViolation => {
159                    #[skip_checked_arithmetic]
160                    tracing::error!(
161                        kind = ?error.kind(),
162                        tx_digest = ?transaction_digest,
163                        "INVARIANT VIOLATION! Source: {:?}",
164                        error.source(),
165                    );
166                }
167
168                K::IotaMoveVerificationError | K::VMVerificationOrDeserializationError => {
169                    #[skip_checked_arithmetic]
170                    tracing::debug!(
171                        kind = ?error.kind(),
172                        tx_digest = ?transaction_digest,
173                        "Verification Error. Source: {:?}",
174                        error.source(),
175                    );
176                }
177
178                K::PublishUpgradeMissingDependency | K::PublishUpgradeDependencyDowngrade => {
179                    #[skip_checked_arithmetic]
180                    tracing::debug!(
181                        kind = ?error.kind(),
182                        tx_digest = ?transaction_digest,
183                        "Publish/Upgrade Error. Source: {:?}",
184                        error.source(),
185                    )
186                }
187
188                _ => (),
189            };
190
191            let (status, command) = error.to_execution_status();
192            ExecutionStatus::new_failure(status, command)
193        } else {
194            ExecutionStatus::Success
195        };
196
197        #[skip_checked_arithmetic]
198        trace!(
199            tx_digest = ?transaction_digest,
200            computation_gas_cost = gas_cost_summary.computation_cost,
201            computation_gas_cost_burned = gas_cost_summary.computation_cost_burned,
202            storage_gas_cost = gas_cost_summary.storage_cost,
203            storage_gas_rebate = gas_cost_summary.storage_rebate,
204            "Finished execution of transaction with status {:?}",
205            status
206        );
207
208        // Genesis writes a special digest to indicate that an object was created during
209        // genesis and not written by any normal transaction - remove that from the
210        // dependencies
211        transaction_dependencies.remove(&TransactionDigest::genesis_marker());
212
213        if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
214            temporary_store
215                .check_ownership_invariants(
216                    &transaction_signer,
217                    &mut gas_charger,
218                    &mutable_inputs,
219                    is_epoch_change,
220                )
221                .unwrap()
222        } // else, in dev inspect mode and anything goes--don't check
223
224        let (inner, effects) = temporary_store.into_effects(
225            shared_object_refs,
226            &transaction_digest,
227            transaction_dependencies,
228            gas_cost_summary,
229            status,
230            &mut gas_charger,
231            *epoch_id,
232        );
233
234        (
235            inner,
236            gas_charger.into_gas_status(),
237            effects,
238            execution_result,
239        )
240    }
241
242    /// Function dedicated to the execution of a GenesisTransaction.
243    /// The function creates an `InnerTemporaryStore`, processes the input
244    /// objects, and executes the transaction in unmetered mode using the
245    /// `Genesis` execution mode. It returns an inner temporary store that
246    /// contains the objects found into the input `GenesisTransaction` by
247    /// adding the data for `previous_transaction` and `storage_rebate` fields.
248    pub fn execute_genesis_state_update(
249        store: &dyn BackingStore,
250        protocol_config: &ProtocolConfig,
251        metrics: Arc<LimitsMetrics>,
252        move_vm: &Arc<MoveVM>,
253        tx_context: &mut TxContext,
254        input_objects: CheckedInputObjects,
255        pt: ProgrammableTransaction,
256    ) -> Result<InnerTemporaryStore, ExecutionError> {
257        let input_objects = input_objects.into_inner();
258        let mut temporary_store = TemporaryStore::new(
259            store,
260            input_objects,
261            vec![],
262            tx_context.digest(),
263            protocol_config,
264            0,
265        );
266        let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
267        programmable_transactions::execution::execute::<execution_mode::Genesis>(
268            protocol_config,
269            metrics,
270            move_vm,
271            &mut temporary_store,
272            tx_context,
273            &mut gas_charger,
274            pt,
275            &mut None,
276        )?;
277        temporary_store.update_object_version_and_prev_tx();
278        Ok(temporary_store.into_inner())
279    }
280
281    /// Executes a transaction by processing the specified `TransactionKind`,
282    /// applying the necessary gas charges and running the main execution logic.
283    /// The function handles certain error conditions such as denied
284    /// certificate, deleted input objects, exceeded execution meter limits,
285    /// failed conservation checks. It also accounts for unmetered storage
286    /// rebates and adjusts for special cases like epoch change
287    /// transactions. Gas costs are managed through the `GasCharger`
288    /// argument; gas is also charged in case of errors.
289    #[instrument(name = "tx_execute", level = "debug", skip_all)]
290    fn execute_transaction<Mode: ExecutionMode>(
291        temporary_store: &mut TemporaryStore<'_>,
292        transaction_kind: TransactionKind,
293        gas_charger: &mut GasCharger,
294        tx_ctx: &mut TxContext,
295        move_vm: &Arc<MoveVM>,
296        protocol_config: &ProtocolConfig,
297        metrics: Arc<LimitsMetrics>,
298        enable_expensive_checks: bool,
299        deny_cert: bool,
300        contains_deleted_input: bool,
301        cancelled_objects: Option<(Vec<ObjectID>, SequenceNumber)>,
302        trace_builder_opt: &mut Option<MoveTraceBuilder>,
303    ) -> (
304        GasCostSummary,
305        Result<Mode::ExecutionResults, ExecutionError>,
306    ) {
307        gas_charger.smash_gas(temporary_store);
308
309        // At this point no charges have been applied yet
310        debug_assert!(
311            gas_charger.no_charges(),
312            "No gas charges must be applied yet"
313        );
314
315        let is_genesis_or_epoch_change_tx = matches!(transaction_kind, TransactionKind::Genesis(_))
316            || transaction_kind.is_end_of_epoch_tx();
317
318        let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
319
320        // We must charge object read here during transaction execution, because if this
321        // fails we must still ensure an effect is committed and all objects
322        // versions incremented
323        let result = gas_charger.charge_input_objects(temporary_store);
324        let mut result = result.and_then(|()| {
325            let mut execution_result = if deny_cert {
326                Err(ExecutionError::new(
327                    ExecutionErrorKind::CertificateDenied,
328                    None,
329                ))
330            } else if contains_deleted_input {
331                Err(ExecutionError::new(
332                    ExecutionErrorKind::InputObjectDeleted,
333                    None,
334                ))
335            } else if let Some((cancelled_objects, reason)) = cancelled_objects {
336                match reason {
337                    SequenceNumber::CONGESTED => Err(ExecutionError::new(
338                        ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
339                            congested_objects: CongestedObjects(cancelled_objects),
340                        },
341                        None,
342                    )),
343                    SequenceNumber::RANDOMNESS_UNAVAILABLE => Err(ExecutionError::new(
344                        ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable,
345                        None,
346                    )),
347                    _ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
348                }
349            } else {
350                execution_loop::<Mode>(
351                    temporary_store,
352                    transaction_kind,
353                    tx_ctx,
354                    move_vm,
355                    gas_charger,
356                    protocol_config,
357                    metrics.clone(),
358                    trace_builder_opt,
359                )
360            };
361
362            let meter_check = check_meter_limit(
363                temporary_store,
364                gas_charger,
365                protocol_config,
366                metrics.clone(),
367            );
368            if let Err(e) = meter_check {
369                execution_result = Err(e);
370            }
371
372            if execution_result.is_ok() {
373                let gas_check = check_written_objects_limit::<Mode>(
374                    temporary_store,
375                    gas_charger,
376                    protocol_config,
377                    metrics,
378                );
379                if let Err(e) = gas_check {
380                    execution_result = Err(e);
381                }
382            }
383
384            execution_result
385        });
386
387        let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
388        // For advance epoch transaction, we need to provide epoch rewards and rebates
389        // as extra information provided to check_iota_conserved, because we
390        // mint rewards, and burn the rebates. We also need to pass in the
391        // unmetered_storage_rebate because storage rebate is not reflected in
392        // the storage_rebate of gas summary. This is a bit confusing.
393        // We could probably clean up the code a bit.
394        // Put all the storage rebate accumulated in the system transaction
395        // to the 0x5 object so that it's not lost.
396        temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
397
398        if let Err(e) = run_conservation_checks::<Mode>(
399            temporary_store,
400            gas_charger,
401            tx_ctx,
402            move_vm,
403            enable_expensive_checks,
404            &cost_summary,
405            is_genesis_or_epoch_change_tx,
406            advance_epoch_gas_summary,
407        ) {
408            // FIXME: we cannot fail the transaction if this is an epoch change transaction.
409            result = Err(e);
410        }
411
412        (cost_summary, result)
413    }
414
415    /// Performs IOTA conservation checks during transaction execution, ensuring
416    /// that the transaction does not create or destroy IOTA. If
417    /// conservation is violated, the function attempts to recover
418    /// by resetting the gas charger, recharging gas, and rechecking
419    /// conservation. If recovery fails, it panics to avoid IOTA creation or
420    /// destruction. These checks include both simple and expensive
421    /// checks based on the configuration and are skipped for genesis or epoch
422    /// change transactions.
423    #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
424    fn run_conservation_checks<Mode: ExecutionMode>(
425        temporary_store: &mut TemporaryStore<'_>,
426        gas_charger: &mut GasCharger,
427        tx_ctx: &mut TxContext,
428        move_vm: &Arc<MoveVM>,
429        enable_expensive_checks: bool,
430        cost_summary: &GasCostSummary,
431        is_genesis_or_epoch_change_tx: bool,
432        advance_epoch_gas_summary: Option<(u64, u64)>,
433    ) -> Result<(), ExecutionError> {
434        let mut result: std::result::Result<(), iota_types::error::ExecutionError> = Ok(());
435        if !is_genesis_or_epoch_change_tx && !Mode::skip_conservation_checks() {
436            // ensure that this transaction did not create or destroy IOTA, try to recover
437            // if the check fails
438            let conservation_result = {
439                temporary_store
440                    .check_iota_conserved(cost_summary)
441                    .and_then(|()| {
442                        if enable_expensive_checks {
443                            // ensure that this transaction did not create or destroy IOTA, try to
444                            // recover if the check fails
445                            let mut layout_resolver =
446                                TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
447                            temporary_store.check_iota_conserved_expensive(
448                                cost_summary,
449                                advance_epoch_gas_summary,
450                                &mut layout_resolver,
451                            )
452                        } else {
453                            Ok(())
454                        }
455                    })
456            };
457            if let Err(conservation_err) = conservation_result {
458                // conservation violated. try to avoid panic by dumping all writes, charging for
459                // gas, re-checking conservation, and surfacing an aborted
460                // transaction with an invariant violation if all of that works
461                result = Err(conservation_err);
462                gas_charger.reset(temporary_store);
463                gas_charger.charge_gas(temporary_store, &mut result);
464                // check conservation once more more
465                if let Err(recovery_err) = {
466                    temporary_store
467                        .check_iota_conserved(cost_summary)
468                        .and_then(|()| {
469                            if enable_expensive_checks {
470                                // ensure that this transaction did not create or destroy IOTA, try
471                                // to recover if the check fails
472                                let mut layout_resolver =
473                                    TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
474                                temporary_store.check_iota_conserved_expensive(
475                                    cost_summary,
476                                    advance_epoch_gas_summary,
477                                    &mut layout_resolver,
478                                )
479                            } else {
480                                Ok(())
481                            }
482                        })
483                } {
484                    // if we still fail, it's a problem with gas
485                    // charging that happens even in the "aborted" case--no other option but panic.
486                    // we will create or destroy IOTA otherwise
487                    panic!(
488                        "IOTA conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
489                        tx_ctx.digest(),
490                        recovery_err,
491                        gas_charger.summary()
492                    )
493                }
494            }
495        } // else, we're in the genesis transaction which mints the IOTA supply, and hence
496        // does not satisfy IOTA conservation, or we're in the non-production
497        // dev inspect mode which allows us to violate conservation
498        result
499    }
500
501    /// Checks if the estimated size of transaction effects exceeds predefined
502    /// limits based on the protocol configuration. For metered
503    /// transactions, it enforces hard limits, while for system transactions, it
504    /// allows soft limits with warnings.
505    #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
506    fn check_meter_limit(
507        temporary_store: &mut TemporaryStore<'_>,
508        gas_charger: &mut GasCharger,
509        protocol_config: &ProtocolConfig,
510        metrics: Arc<LimitsMetrics>,
511    ) -> Result<(), ExecutionError> {
512        let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
513
514        // Check if a limit threshold was crossed.
515        // For metered transactions, there is not soft limit.
516        // For system transactions, we allow a soft limit with alerting, and a hard
517        // limit where we terminate
518        match check_limit_by_meter!(
519            !gas_charger.is_unmetered(),
520            effects_estimated_size,
521            protocol_config.max_serialized_tx_effects_size_bytes(),
522            protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
523            metrics.excessive_estimated_effects_size
524        ) {
525            LimitThresholdCrossed::None => Ok(()),
526            LimitThresholdCrossed::Soft(_, limit) => {
527                warn!(
528                    effects_estimated_size = effects_estimated_size,
529                    soft_limit = limit,
530                    "Estimated transaction effects size crossed soft limit",
531                );
532                Ok(())
533            }
534            LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
535                ExecutionErrorKind::EffectsTooLarge {
536                    current_size: effects_estimated_size as u64,
537                    max_size: lim as u64,
538                },
539                "Transaction effects are too large",
540            )),
541        }
542    }
543
544    /// Checks if the total size of written objects in the transaction exceeds
545    /// the limits defined in the protocol configuration. For metered
546    /// transactions, it enforces a hard limit, while for system transactions,
547    /// it allows a soft limit with warnings.
548    #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
549    fn check_written_objects_limit<Mode: ExecutionMode>(
550        temporary_store: &mut TemporaryStore<'_>,
551        gas_charger: &mut GasCharger,
552        protocol_config: &ProtocolConfig,
553        metrics: Arc<LimitsMetrics>,
554    ) -> Result<(), ExecutionError> {
555        if let (Some(normal_lim), Some(system_lim)) = (
556            protocol_config.max_size_written_objects_as_option(),
557            protocol_config.max_size_written_objects_system_tx_as_option(),
558        ) {
559            let written_objects_size = temporary_store.written_objects_size();
560
561            match check_limit_by_meter!(
562                !gas_charger.is_unmetered(),
563                written_objects_size,
564                normal_lim,
565                system_lim,
566                metrics.excessive_written_objects_size
567            ) {
568                LimitThresholdCrossed::None => (),
569                LimitThresholdCrossed::Soft(_, limit) => {
570                    warn!(
571                        written_objects_size = written_objects_size,
572                        soft_limit = limit,
573                        "Written objects size crossed soft limit",
574                    )
575                }
576                LimitThresholdCrossed::Hard(_, lim) => {
577                    return Err(ExecutionError::new_with_source(
578                        ExecutionErrorKind::WrittenObjectsTooLarge {
579                            current_size: written_objects_size as u64,
580                            max_size: lim as u64,
581                        },
582                        "Written objects size crossed hard limit",
583                    ));
584                }
585            };
586        }
587
588        Ok(())
589    }
590
591    /// Executes the given transaction based on its `TransactionKind` by
592    /// processing it through corresponding handlers such as epoch changes,
593    /// genesis transactions, consensus commit prologues, and programmable
594    /// transactions. For each type of transaction, the corresponding logic is
595    /// invoked, such as advancing the epoch, setting up consensus commits, or
596    /// executing a programmable transaction.
597    #[instrument(level = "debug", skip_all)]
598    fn execution_loop<Mode: ExecutionMode>(
599        temporary_store: &mut TemporaryStore<'_>,
600        transaction_kind: TransactionKind,
601        tx_ctx: &mut TxContext,
602        move_vm: &Arc<MoveVM>,
603        gas_charger: &mut GasCharger,
604        protocol_config: &ProtocolConfig,
605        metrics: Arc<LimitsMetrics>,
606        trace_builder_opt: &mut Option<MoveTraceBuilder>,
607    ) -> Result<Mode::ExecutionResults, ExecutionError> {
608        let result = match transaction_kind {
609            TransactionKind::Genesis(GenesisTransaction { objects, events }) => {
610                if tx_ctx.epoch() != 0 {
611                    panic!("BUG: Genesis Transactions can only be executed in epoch 0");
612                }
613
614                for genesis_object in objects {
615                    match genesis_object {
616                        iota_types::transaction::GenesisObject::RawObject { data, owner } => {
617                            let object = ObjectInner {
618                                data,
619                                owner,
620                                previous_transaction: tx_ctx.digest(),
621                                storage_rebate: 0,
622                            };
623                            temporary_store.create_object(object.into());
624                        }
625                    }
626                }
627
628                temporary_store.record_execution_results(ExecutionResults::V1(
629                    ExecutionResultsV1 {
630                        user_events: events,
631                        ..Default::default()
632                    },
633                ));
634
635                Ok(Mode::empty_results())
636            }
637            TransactionKind::ConsensusCommitPrologueV1(prologue) => {
638                setup_consensus_commit(
639                    prologue.commit_timestamp_ms,
640                    temporary_store,
641                    tx_ctx,
642                    move_vm,
643                    gas_charger,
644                    protocol_config,
645                    metrics,
646                    trace_builder_opt,
647                )
648                .expect("ConsensusCommitPrologueV1 cannot fail");
649                Ok(Mode::empty_results())
650            }
651            TransactionKind::ProgrammableTransaction(pt) => {
652                programmable_transactions::execution::execute::<Mode>(
653                    protocol_config,
654                    metrics,
655                    move_vm,
656                    temporary_store,
657                    tx_ctx,
658                    gas_charger,
659                    pt,
660                    trace_builder_opt,
661                )
662            }
663            TransactionKind::EndOfEpochTransaction(txns) => {
664                let mut builder = ProgrammableTransactionBuilder::new();
665                let len = txns.len();
666                for (i, tx) in txns.into_iter().enumerate() {
667                    match tx {
668                        EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
669                            assert_eq!(i, len - 1);
670                            advance_epoch_v1(
671                                builder,
672                                change_epoch,
673                                temporary_store,
674                                tx_ctx,
675                                move_vm,
676                                gas_charger,
677                                protocol_config,
678                                metrics,
679                                trace_builder_opt,
680                            )?;
681                            return Ok(Mode::empty_results());
682                        }
683                        EndOfEpochTransactionKind::ChangeEpochV2(change_epoch_v2) => {
684                            assert_eq!(i, len - 1);
685                            advance_epoch_v2(
686                                builder,
687                                change_epoch_v2,
688                                temporary_store,
689                                tx_ctx,
690                                move_vm,
691                                gas_charger,
692                                protocol_config,
693                                metrics,
694                                trace_builder_opt,
695                            )?;
696                            return Ok(Mode::empty_results());
697                        }
698                        EndOfEpochTransactionKind::AuthenticatorStateCreate => {
699                            assert!(protocol_config.enable_jwk_consensus_updates());
700                            builder = setup_authenticator_state_create(builder);
701                        }
702                        EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
703                            assert!(protocol_config.enable_jwk_consensus_updates());
704
705                            // TODO: it would be nice if a failure of this function didn't cause
706                            // safe mode.
707                            builder = setup_authenticator_state_expire(builder, expire);
708                        }
709                    }
710                }
711                unreachable!(
712                    "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
713                )
714            }
715            TransactionKind::AuthenticatorStateUpdateV1(auth_state_update) => {
716                setup_authenticator_state_update(
717                    auth_state_update,
718                    temporary_store,
719                    tx_ctx,
720                    move_vm,
721                    gas_charger,
722                    protocol_config,
723                    metrics,
724                    trace_builder_opt,
725                )?;
726                Ok(Mode::empty_results())
727            }
728            TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
729                setup_randomness_state_update(
730                    randomness_state_update,
731                    temporary_store,
732                    tx_ctx,
733                    move_vm,
734                    gas_charger,
735                    protocol_config,
736                    metrics,
737                    trace_builder_opt,
738                )?;
739                Ok(Mode::empty_results())
740            }
741        }?;
742        temporary_store.check_execution_results_consistency()?;
743        Ok(result)
744    }
745
746    /// Mints epoch rewards by creating both storage and computation charges
747    /// using a `ProgrammableTransactionBuilder`. The function takes in the
748    /// `AdvanceEpochParams`, serializes the storage and computation
749    /// charges, and invokes the reward creation function within the IOTA
750    /// Prepares invocations for creating both storage and computation charges
751    /// with a `ProgrammableTransactionBuilder` using the `AdvanceEpochParams`.
752    /// The corresponding functions from the IOTA framework can be invoked later
753    /// during execution of the programmable transaction.
754    fn mint_epoch_rewards_in_pt(
755        builder: &mut ProgrammableTransactionBuilder,
756        params: &AdvanceEpochParams,
757    ) -> (Argument, Argument) {
758        // Create storage charges.
759        let storage_charge_arg = builder
760            .input(CallArg::Pure(
761                bcs::to_bytes(&params.storage_charge).unwrap(),
762            ))
763            .unwrap();
764        let storage_charges = builder.programmable_move_call(
765            IOTA_FRAMEWORK_PACKAGE_ID,
766            BALANCE_MODULE_NAME.to_owned(),
767            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
768            vec![GAS::type_tag()],
769            vec![storage_charge_arg],
770        );
771
772        // Create computation charges.
773        let computation_charge_arg = builder
774            .input(CallArg::Pure(
775                bcs::to_bytes(&params.computation_charge).unwrap(),
776            ))
777            .unwrap();
778        let computation_charges = builder.programmable_move_call(
779            IOTA_FRAMEWORK_PACKAGE_ID,
780            BALANCE_MODULE_NAME.to_owned(),
781            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
782            vec![GAS::type_tag()],
783            vec![computation_charge_arg],
784        );
785        (storage_charges, computation_charges)
786    }
787
788    /// Constructs a `ProgrammableTransaction` to advance the epoch. It creates
789    /// storage charges and computation charges by invoking
790    /// `mint_epoch_rewards_in_pt`, advances the epoch by setting up the
791    /// necessary arguments, such as epoch number, protocol version, storage
792    /// rebate, and slashing rate, and executing the `advance_epoch` function
793    /// within the IOTA system. Then, it destroys the storage rebates to
794    /// complete the transaction.
795    pub fn construct_advance_epoch_pt_impl(
796        mut builder: ProgrammableTransactionBuilder,
797        params: &AdvanceEpochParams,
798        call_arg_vec: Vec<CallArg>,
799    ) -> Result<ProgrammableTransaction, ExecutionError> {
800        // Create storage and computation charges and add them as arguments.
801        let (storage_charges, computation_charges) = mint_epoch_rewards_in_pt(&mut builder, params);
802        let mut arguments = vec![
803            builder
804                .pure(params.validator_subsidy)
805                .expect("bcs encoding a u64 should not fail"),
806            storage_charges,
807            computation_charges,
808        ];
809
810        let call_arg_arguments = call_arg_vec
811            .into_iter()
812            .map(|a| builder.input(a))
813            .collect::<Result<_, _>>();
814
815        assert_invariant!(
816            call_arg_arguments.is_ok(),
817            "Unable to generate args for advance_epoch transaction!"
818        );
819
820        arguments.append(&mut call_arg_arguments.unwrap());
821
822        info!("Call arguments to advance_epoch transaction: {:?}", params);
823
824        let storage_rebates = builder.programmable_move_call(
825            IOTA_SYSTEM_PACKAGE_ID,
826            IOTA_SYSTEM_MODULE_NAME.to_owned(),
827            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
828            vec![],
829            arguments,
830        );
831
832        // Step 3: Destroy the storage rebates.
833        builder.programmable_move_call(
834            IOTA_FRAMEWORK_PACKAGE_ID,
835            BALANCE_MODULE_NAME.to_owned(),
836            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
837            vec![GAS::type_tag()],
838            vec![storage_rebates],
839        );
840        Ok(builder.finish())
841    }
842
843    pub fn construct_advance_epoch_pt_v1(
844        builder: ProgrammableTransactionBuilder,
845        params: &AdvanceEpochParams,
846    ) -> Result<ProgrammableTransaction, ExecutionError> {
847        // the first three arguments to the advance_epoch function, namely
848        // validator_subsidy, storage_charges and computation_charges, are
849        // common to both v1 and v2 and are added in `construct_advance_epoch_pt_impl`.
850        // The remaining arguments are added here.
851        let call_arg_vec = vec![
852            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
853            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
854            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
855            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
856            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
857            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
858            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
859        ];
860        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
861    }
862
863    pub fn construct_advance_epoch_pt_v2(
864        builder: ProgrammableTransactionBuilder,
865        params: &AdvanceEpochParams,
866    ) -> Result<ProgrammableTransaction, ExecutionError> {
867        // the first three arguments to the advance_epoch function, namely
868        // validator_subsidy, storage_charges and computation_charges, are
869        // common to both v1 and v2 and are added in `construct_advance_epoch_pt_impl`.
870        // The remaining arguments are added here.
871        let call_arg_vec = vec![
872            CallArg::Pure(bcs::to_bytes(&params.computation_charge_burned).unwrap()), /* computation_charge_burned: u64 */
873            CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
874            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
875            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
876            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
877            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
878            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
879            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
880            CallArg::Pure(bcs::to_bytes(&params.max_committee_members_count).unwrap()), /* max_committee_members_count: u64 */
881        ];
882        construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
883    }
884
885    /// Advances the epoch by executing a `ProgrammableTransaction`. If the
886    /// transaction fails, it switches to safe mode and retries the epoch
887    /// advancement in a more controlled environment. The function also
888    /// handles the publication and upgrade of system packages for the new
889    /// epoch. If any system package is added or upgraded, it ensures the
890    /// proper execution and storage of the changes.
891    fn advance_epoch_impl(
892        advance_epoch_pt: ProgrammableTransaction,
893        params: AdvanceEpochParams,
894        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
895        temporary_store: &mut TemporaryStore<'_>,
896        tx_ctx: &mut TxContext,
897        move_vm: &Arc<MoveVM>,
898        gas_charger: &mut GasCharger,
899        protocol_config: &ProtocolConfig,
900        metrics: Arc<LimitsMetrics>,
901        trace_builder_opt: &mut Option<MoveTraceBuilder>,
902    ) -> Result<(), ExecutionError> {
903        let result = programmable_transactions::execution::execute::<execution_mode::System>(
904            protocol_config,
905            metrics.clone(),
906            move_vm,
907            temporary_store,
908            tx_ctx,
909            gas_charger,
910            advance_epoch_pt,
911            trace_builder_opt,
912        );
913
914        #[cfg(msim)]
915        let result = maybe_modify_result(result, params.epoch);
916
917        if result.is_err() {
918            tracing::error!(
919                "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx params: {:?}",
920                result.as_ref().err(),
921                temporary_store.objects(),
922                params,
923            );
924            temporary_store.drop_writes();
925            // Must reset the storage rebate since we are re-executing.
926            gas_charger.reset_storage_cost_and_rebate();
927
928            temporary_store.advance_epoch_safe_mode(&params, protocol_config);
929        }
930
931        let new_vm = new_move_vm(
932            all_natives(/* silent */ true, protocol_config),
933            protocol_config,
934            // enable_profiler
935            None,
936        )
937        .expect("Failed to create new MoveVM");
938        process_system_packages(
939            system_packages,
940            temporary_store,
941            tx_ctx,
942            &new_vm,
943            gas_charger,
944            protocol_config,
945            metrics,
946            trace_builder_opt,
947        );
948
949        Ok(())
950    }
951
952    /// Advances the epoch for the given `ChangeEpoch` transaction kind by
953    /// constructing a programmable transaction, executing it and processing the
954    /// system packages.
955    fn advance_epoch_v1(
956        builder: ProgrammableTransactionBuilder,
957        change_epoch: ChangeEpoch,
958        temporary_store: &mut TemporaryStore<'_>,
959        tx_ctx: &mut TxContext,
960        move_vm: &Arc<MoveVM>,
961        gas_charger: &mut GasCharger,
962        protocol_config: &ProtocolConfig,
963        metrics: Arc<LimitsMetrics>,
964        trace_builder_opt: &mut Option<MoveTraceBuilder>,
965    ) -> Result<(), ExecutionError> {
966        let params = AdvanceEpochParams {
967            epoch: change_epoch.epoch,
968            next_protocol_version: change_epoch.protocol_version,
969            validator_subsidy: protocol_config.validator_target_reward(),
970            storage_charge: change_epoch.storage_charge,
971            computation_charge: change_epoch.computation_charge,
972            // all computation charge is burned in v1
973            computation_charge_burned: change_epoch.computation_charge,
974            storage_rebate: change_epoch.storage_rebate,
975            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
976            reward_slashing_rate: protocol_config.reward_slashing_rate(),
977            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
978            // AdvanceEpochV1 does not use this field, but keeping it to avoid creating a separate
979            // AdvanceEpochParams struct.
980            max_committee_members_count: 0,
981        };
982        let advance_epoch_pt = construct_advance_epoch_pt_v1(builder, &params)?;
983        advance_epoch_impl(
984            advance_epoch_pt,
985            params,
986            change_epoch.system_packages,
987            temporary_store,
988            tx_ctx,
989            move_vm,
990            gas_charger,
991            protocol_config,
992            metrics,
993            trace_builder_opt,
994        )
995    }
996
997    /// Advances the epoch for the given `ChangeEpochV2` transaction kind by
998    /// constructing a programmable transaction, executing it and processing the
999    /// system packages.
1000    fn advance_epoch_v2(
1001        builder: ProgrammableTransactionBuilder,
1002        change_epoch_v2: ChangeEpochV2,
1003        temporary_store: &mut TemporaryStore<'_>,
1004        tx_ctx: &mut TxContext,
1005        move_vm: &Arc<MoveVM>,
1006        gas_charger: &mut GasCharger,
1007        protocol_config: &ProtocolConfig,
1008        metrics: Arc<LimitsMetrics>,
1009        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1010    ) -> Result<(), ExecutionError> {
1011        let params = AdvanceEpochParams {
1012            epoch: change_epoch_v2.epoch,
1013            next_protocol_version: change_epoch_v2.protocol_version,
1014            validator_subsidy: protocol_config.validator_target_reward(),
1015            storage_charge: change_epoch_v2.storage_charge,
1016            computation_charge: change_epoch_v2.computation_charge,
1017            computation_charge_burned: change_epoch_v2.computation_charge_burned,
1018            storage_rebate: change_epoch_v2.storage_rebate,
1019            non_refundable_storage_fee: change_epoch_v2.non_refundable_storage_fee,
1020            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1021            epoch_start_timestamp_ms: change_epoch_v2.epoch_start_timestamp_ms,
1022            max_committee_members_count: protocol_config.max_committee_members_count(),
1023        };
1024        let advance_epoch_pt = construct_advance_epoch_pt_v2(builder, &params)?;
1025        advance_epoch_impl(
1026            advance_epoch_pt,
1027            params,
1028            change_epoch_v2.system_packages,
1029            temporary_store,
1030            tx_ctx,
1031            move_vm,
1032            gas_charger,
1033            protocol_config,
1034            metrics,
1035            trace_builder_opt,
1036        )
1037    }
1038
1039    fn process_system_packages(
1040        system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
1041        temporary_store: &mut TemporaryStore<'_>,
1042        tx_ctx: &mut TxContext,
1043        move_vm: &MoveVM,
1044        gas_charger: &mut GasCharger,
1045        protocol_config: &ProtocolConfig,
1046        metrics: Arc<LimitsMetrics>,
1047        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1048    ) {
1049        let binary_config = to_binary_config(protocol_config);
1050        for (version, modules, dependencies) in system_packages.into_iter() {
1051            let deserialized_modules: Vec<_> = modules
1052                .iter()
1053                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
1054                .collect();
1055
1056            if version == OBJECT_START_VERSION {
1057                let package_id = deserialized_modules.first().unwrap().address();
1058                info!("adding new system package {package_id}");
1059
1060                let publish_pt = {
1061                    let mut b = ProgrammableTransactionBuilder::new();
1062                    b.command(Command::Publish(modules, dependencies));
1063                    b.finish()
1064                };
1065
1066                programmable_transactions::execution::execute::<execution_mode::System>(
1067                    protocol_config,
1068                    metrics.clone(),
1069                    move_vm,
1070                    temporary_store,
1071                    tx_ctx,
1072                    gas_charger,
1073                    publish_pt,
1074                    trace_builder_opt,
1075                )
1076                .expect("System Package Publish must succeed");
1077            } else {
1078                let mut new_package = Object::new_system_package(
1079                    &deserialized_modules,
1080                    version,
1081                    dependencies,
1082                    tx_ctx.digest(),
1083                );
1084
1085                info!(
1086                    "upgraded system package {:?}",
1087                    new_package.compute_object_reference()
1088                );
1089
1090                // Decrement the version before writing the package so that the store can record
1091                // the version growing by one in the effects.
1092                new_package
1093                    .data
1094                    .try_as_package_mut()
1095                    .unwrap()
1096                    .decrement_version();
1097
1098                // upgrade of a previously existing framework module
1099                temporary_store.upgrade_system_package(new_package);
1100            }
1101        }
1102    }
1103
1104    /// Perform metadata updates in preparation for the transactions in the
1105    /// upcoming checkpoint:
1106    ///
1107    /// - Set the timestamp for the `Clock` shared object from the timestamp in
1108    ///   the header from consensus.
1109    fn setup_consensus_commit(
1110        consensus_commit_timestamp_ms: CheckpointTimestamp,
1111        temporary_store: &mut TemporaryStore<'_>,
1112        tx_ctx: &mut TxContext,
1113        move_vm: &Arc<MoveVM>,
1114        gas_charger: &mut GasCharger,
1115        protocol_config: &ProtocolConfig,
1116        metrics: Arc<LimitsMetrics>,
1117        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1118    ) -> Result<(), ExecutionError> {
1119        let pt = {
1120            let mut builder = ProgrammableTransactionBuilder::new();
1121            let res = builder.move_call(
1122                IOTA_FRAMEWORK_ADDRESS.into(),
1123                CLOCK_MODULE_NAME.to_owned(),
1124                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
1125                vec![],
1126                vec![
1127                    CallArg::CLOCK_MUT,
1128                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
1129                ],
1130            );
1131            assert_invariant!(
1132                res.is_ok(),
1133                "Unable to generate consensus_commit_prologue transaction!"
1134            );
1135            builder.finish()
1136        };
1137        programmable_transactions::execution::execute::<execution_mode::System>(
1138            protocol_config,
1139            metrics,
1140            move_vm,
1141            temporary_store,
1142            tx_ctx,
1143            gas_charger,
1144            pt,
1145            trace_builder_opt,
1146        )
1147    }
1148
1149    /// This function adds a Move call to the IOTA framework's
1150    /// `authenticator_state_create` function, preparing the transaction for
1151    /// execution.
1152    fn setup_authenticator_state_create(
1153        mut builder: ProgrammableTransactionBuilder,
1154    ) -> ProgrammableTransactionBuilder {
1155        builder
1156            .move_call(
1157                IOTA_FRAMEWORK_ADDRESS.into(),
1158                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1159                AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1160                vec![],
1161                vec![],
1162            )
1163            .expect("Unable to generate authenticator_state_create transaction!");
1164        builder
1165    }
1166
1167    /// Sets up and executes a `ProgrammableTransaction` to update the
1168    /// authenticator state. This function constructs a transaction that
1169    /// invokes the `authenticator_state_update` function from the IOTA
1170    /// framework, passing the authenticator state object and new active JWKS as
1171    /// arguments. It then executes the transaction using the system
1172    /// execution mode.
1173    fn setup_authenticator_state_update(
1174        update: AuthenticatorStateUpdateV1,
1175        temporary_store: &mut TemporaryStore<'_>,
1176        tx_ctx: &mut TxContext,
1177        move_vm: &Arc<MoveVM>,
1178        gas_charger: &mut GasCharger,
1179        protocol_config: &ProtocolConfig,
1180        metrics: Arc<LimitsMetrics>,
1181        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1182    ) -> Result<(), ExecutionError> {
1183        let pt = {
1184            let mut builder = ProgrammableTransactionBuilder::new();
1185            let res = builder.move_call(
1186                IOTA_FRAMEWORK_ADDRESS.into(),
1187                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1188                AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1189                vec![],
1190                vec![
1191                    CallArg::Object(ObjectArg::SharedObject {
1192                        id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1193                        initial_shared_version: update.authenticator_obj_initial_shared_version,
1194                        mutable: true,
1195                    }),
1196                    CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1197                ],
1198            );
1199            assert_invariant!(
1200                res.is_ok(),
1201                "Unable to generate authenticator_state_update transaction!"
1202            );
1203            builder.finish()
1204        };
1205        programmable_transactions::execution::execute::<execution_mode::System>(
1206            protocol_config,
1207            metrics,
1208            move_vm,
1209            temporary_store,
1210            tx_ctx,
1211            gas_charger,
1212            pt,
1213            trace_builder_opt,
1214        )
1215    }
1216
1217    /// Configures a `ProgrammableTransactionBuilder` to expire authenticator
1218    /// state by invoking the `authenticator_state_expire_jwks` function
1219    /// from the IOTA framework. The function adds the necessary Move call
1220    /// with the authenticator state object and the minimum epoch as arguments.
1221    fn setup_authenticator_state_expire(
1222        mut builder: ProgrammableTransactionBuilder,
1223        expire: AuthenticatorStateExpire,
1224    ) -> ProgrammableTransactionBuilder {
1225        builder
1226            .move_call(
1227                IOTA_FRAMEWORK_ADDRESS.into(),
1228                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1229                AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1230                vec![],
1231                vec![
1232                    CallArg::Object(ObjectArg::SharedObject {
1233                        id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1234                        initial_shared_version: expire.authenticator_obj_initial_shared_version,
1235                        mutable: true,
1236                    }),
1237                    CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1238                ],
1239            )
1240            .expect("Unable to generate authenticator_state_expire transaction!");
1241        builder
1242    }
1243
1244    /// The function constructs a transaction that invokes
1245    /// the `randomness_state_update` function from the IOTA framework,
1246    /// passing the randomness state object, the `randomness_round`,
1247    /// and the `random_bytes` as arguments. It then executes the transaction
1248    /// using the system execution mode.
1249    fn setup_randomness_state_update(
1250        update: RandomnessStateUpdate,
1251        temporary_store: &mut TemporaryStore<'_>,
1252        tx_ctx: &mut TxContext,
1253        move_vm: &Arc<MoveVM>,
1254        gas_charger: &mut GasCharger,
1255        protocol_config: &ProtocolConfig,
1256        metrics: Arc<LimitsMetrics>,
1257        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1258    ) -> Result<(), ExecutionError> {
1259        let pt = {
1260            let mut builder = ProgrammableTransactionBuilder::new();
1261            let res = builder.move_call(
1262                IOTA_FRAMEWORK_ADDRESS.into(),
1263                RANDOMNESS_MODULE_NAME.to_owned(),
1264                RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1265                vec![],
1266                vec![
1267                    CallArg::Object(ObjectArg::SharedObject {
1268                        id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1269                        initial_shared_version: update.randomness_obj_initial_shared_version,
1270                        mutable: true,
1271                    }),
1272                    CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1273                    CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1274                ],
1275            );
1276            assert_invariant!(
1277                res.is_ok(),
1278                "Unable to generate randomness_state_update transaction!"
1279            );
1280            builder.finish()
1281        };
1282        programmable_transactions::execution::execute::<execution_mode::System>(
1283            protocol_config,
1284            metrics,
1285            move_vm,
1286            temporary_store,
1287            tx_ctx,
1288            gas_charger,
1289            pt,
1290            trace_builder_opt,
1291        )
1292    }
1293}