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