sui_adapter_latest/
execution_engine.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8
9    use crate::execution_mode::{self, ExecutionMode};
10    use crate::execution_value::SuiResolver;
11    use move_binary_format::CompiledModule;
12    use move_trace_format::format::MoveTraceBuilder;
13    use move_vm_runtime::move_vm::MoveVM;
14    use mysten_common::debug_fatal;
15    use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};
16    use sui_types::accumulator_root::{ACCUMULATOR_ROOT_CREATE_FUNC, ACCUMULATOR_ROOT_MODULE};
17    use sui_types::balance::{
18        BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
19        BALANCE_MODULE_NAME,
20    };
21    use sui_types::execution_params::ExecutionOrEarlyError;
22    use sui_types::gas_coin::GAS;
23    use sui_types::messages_checkpoint::CheckpointTimestamp;
24    use sui_types::metrics::LimitsMetrics;
25    use sui_types::object::OBJECT_START_VERSION;
26    use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
27    use sui_types::randomness_state::{
28        RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_CREATE_FUNCTION_NAME,
29        RANDOMNESS_STATE_UPDATE_FUNCTION_NAME,
30    };
31    use sui_types::{BRIDGE_ADDRESS, SUI_BRIDGE_OBJECT_ID, SUI_RANDOMNESS_STATE_OBJECT_ID};
32    use tracing::{info, instrument, trace, warn};
33
34    use crate::adapter::new_move_vm;
35    use crate::programmable_transactions;
36    use crate::sui_types::gas::SuiGasStatusAPI;
37    use crate::type_layout_resolver::TypeLayoutResolver;
38    use crate::{gas_charger::GasCharger, temporary_store::TemporaryStore};
39    use move_core_types::ident_str;
40    use sui_move_natives::all_natives;
41    use sui_protocol_config::{
42        LimitThresholdCrossed, PerObjectCongestionControlMode, ProtocolConfig, check_limit_by_meter,
43    };
44    use sui_types::authenticator_state::{
45        AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME, AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME,
46        AUTHENTICATOR_STATE_MODULE_NAME, AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
47    };
48    use sui_types::base_types::SequenceNumber;
49    use sui_types::bridge::BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER;
50    use sui_types::bridge::{
51        BRIDGE_CREATE_FUNCTION_NAME, BRIDGE_INIT_COMMITTEE_FUNCTION_NAME, BRIDGE_MODULE_NAME,
52        BridgeChainId,
53    };
54    use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME};
55    use sui_types::committee::EpochId;
56    use sui_types::deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE};
57    use sui_types::digests::{
58        ChainIdentifier, get_mainnet_chain_identifier, get_testnet_chain_identifier,
59    };
60    use sui_types::effects::TransactionEffects;
61    use sui_types::error::{ExecutionError, ExecutionErrorKind};
62    use sui_types::execution::{ExecutionTiming, ResultWithTimings};
63    use sui_types::execution_status::ExecutionStatus;
64    use sui_types::gas::GasCostSummary;
65    use sui_types::gas::SuiGasStatus;
66    use sui_types::id::UID;
67    use sui_types::inner_temporary_store::InnerTemporaryStore;
68    use sui_types::storage::BackingStore;
69    #[cfg(msim)]
70    use sui_types::sui_system_state::advance_epoch_result_injection::maybe_modify_result;
71    use sui_types::sui_system_state::{ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME, AdvanceEpochParams};
72    use sui_types::transaction::{
73        Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, CallArg, ChangeEpoch,
74        Command, EndOfEpochTransactionKind, GasData, GenesisTransaction, ObjectArg,
75        ProgrammableTransaction, StoredExecutionTimeObservations, TransactionKind,
76        is_gas_paid_from_address_balance,
77    };
78    use sui_types::transaction::{CheckedInputObjects, RandomnessStateUpdate};
79    use sui_types::{
80        SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_FRAMEWORK_PACKAGE_ID,
81        SUI_SYSTEM_PACKAGE_ID,
82        base_types::{SuiAddress, TransactionDigest, TxContext},
83        object::{Object, ObjectInner},
84        sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME},
85    };
86
87    #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
88    pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
89        store: &dyn BackingStore,
90        input_objects: CheckedInputObjects,
91        gas_data: GasData,
92        gas_status: SuiGasStatus,
93        transaction_kind: TransactionKind,
94        transaction_signer: SuiAddress,
95        transaction_digest: TransactionDigest,
96        move_vm: &Arc<MoveVM>,
97        epoch_id: &EpochId,
98        epoch_timestamp_ms: u64,
99        protocol_config: &ProtocolConfig,
100        metrics: Arc<LimitsMetrics>,
101        enable_expensive_checks: bool,
102        execution_params: ExecutionOrEarlyError,
103        trace_builder_opt: &mut Option<MoveTraceBuilder>,
104    ) -> (
105        InnerTemporaryStore,
106        SuiGasStatus,
107        TransactionEffects,
108        Vec<ExecutionTiming>,
109        Result<Mode::ExecutionResults, ExecutionError>,
110    ) {
111        let input_objects = input_objects.into_inner();
112        let mutable_inputs = if enable_expensive_checks {
113            input_objects.all_mutable_inputs().keys().copied().collect()
114        } else {
115            HashSet::new()
116        };
117        let shared_object_refs = input_objects.filter_shared_objects();
118        let receiving_objects = transaction_kind.receiving_objects();
119        let mut transaction_dependencies = input_objects.transaction_dependencies();
120
121        let mut temporary_store = TemporaryStore::new(
122            store,
123            input_objects,
124            receiving_objects,
125            transaction_digest,
126            protocol_config,
127            *epoch_id,
128        );
129
130        let sponsor = {
131            let gas_owner = gas_data.owner;
132            if gas_owner == transaction_signer {
133                None
134            } else {
135                Some(gas_owner)
136            }
137        };
138        let gas_price = gas_status.gas_price();
139        let rgp = gas_status.reference_gas_price();
140        let address_balance_gas_payer =
141            if is_gas_paid_from_address_balance(&gas_data, &transaction_kind) {
142                Some(gas_data.owner)
143            } else {
144                None
145            };
146        let mut gas_charger = GasCharger::new(
147            transaction_digest,
148            gas_data.payment,
149            gas_status,
150            protocol_config,
151            address_balance_gas_payer,
152        );
153
154        let tx_ctx = TxContext::new_from_components(
155            &transaction_signer,
156            &transaction_digest,
157            epoch_id,
158            epoch_timestamp_ms,
159            rgp,
160            gas_price,
161            gas_data.budget,
162            sponsor,
163            protocol_config,
164        );
165        let tx_ctx = Rc::new(RefCell::new(tx_ctx));
166
167        let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
168
169        let (gas_cost_summary, execution_result, timings) = execute_transaction::<Mode>(
170            store,
171            &mut temporary_store,
172            transaction_kind,
173            &mut gas_charger,
174            tx_ctx,
175            move_vm,
176            protocol_config,
177            metrics,
178            enable_expensive_checks,
179            execution_params,
180            trace_builder_opt,
181        );
182
183        let status = if let Err(error) = &execution_result {
184            // Elaborate errors in logs if they are unexpected or their status is terse.
185            use ExecutionErrorKind as K;
186            match error.kind() {
187                K::InvariantViolation | K::VMInvariantViolation => {
188                    if protocol_config.debug_fatal_on_move_invariant_violation() {
189                        debug_fatal!(
190                            "INVARIANT VIOLATION! Txn Digest: {}, Source: {:?}",
191                            transaction_digest,
192                            error.source(),
193                        );
194                    } else {
195                        #[skip_checked_arithmetic]
196                        tracing::error!(
197                            kind = ?error.kind(),
198                            tx_digest = ?transaction_digest,
199                            "INVARIANT VIOLATION! Source: {:?}",
200                            error.source(),
201                        );
202                    }
203                }
204
205                K::SuiMoveVerificationError | K::VMVerificationOrDeserializationError => {
206                    #[skip_checked_arithmetic]
207                    tracing::debug!(
208                        kind = ?error.kind(),
209                        tx_digest = ?transaction_digest,
210                        "Verification Error. Source: {:?}",
211                        error.source(),
212                    );
213                }
214
215                K::PublishUpgradeMissingDependency | K::PublishUpgradeDependencyDowngrade => {
216                    #[skip_checked_arithmetic]
217                    tracing::debug!(
218                        kind = ?error.kind(),
219                        tx_digest = ?transaction_digest,
220                        "Publish/Upgrade Error. Source: {:?}",
221                        error.source(),
222                    )
223                }
224
225                _ => (),
226            };
227
228            let (status, command) = error.to_execution_status();
229            ExecutionStatus::new_failure(status, command)
230        } else {
231            ExecutionStatus::Success
232        };
233
234        #[skip_checked_arithmetic]
235        trace!(
236            tx_digest = ?transaction_digest,
237            computation_gas_cost = gas_cost_summary.computation_cost,
238            storage_gas_cost = gas_cost_summary.storage_cost,
239            storage_gas_rebate = gas_cost_summary.storage_rebate,
240            "Finished execution of transaction with status {:?}",
241            status
242        );
243
244        // Genesis writes a special digest to indicate that an object was created during
245        // genesis and not written by any normal transaction - remove that from the
246        // dependencies
247        transaction_dependencies.remove(&TransactionDigest::genesis_marker());
248
249        if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
250            temporary_store
251                .check_ownership_invariants(
252                    &transaction_signer,
253                    &sponsor,
254                    &mut gas_charger,
255                    &mutable_inputs,
256                    is_epoch_change,
257                )
258                .unwrap()
259        } // else, in dev inspect mode and anything goes--don't check
260
261        let (inner, effects) = temporary_store.into_effects(
262            shared_object_refs,
263            &transaction_digest,
264            transaction_dependencies,
265            gas_cost_summary,
266            status,
267            &mut gas_charger,
268            *epoch_id,
269        );
270
271        (
272            inner,
273            gas_charger.into_gas_status(),
274            effects,
275            timings,
276            execution_result,
277        )
278    }
279
280    pub fn execute_genesis_state_update(
281        store: &dyn BackingStore,
282        protocol_config: &ProtocolConfig,
283        metrics: Arc<LimitsMetrics>,
284        move_vm: &Arc<MoveVM>,
285        tx_context: Rc<RefCell<TxContext>>,
286        input_objects: CheckedInputObjects,
287        pt: ProgrammableTransaction,
288    ) -> Result<InnerTemporaryStore, ExecutionError> {
289        let input_objects = input_objects.into_inner();
290        let mut temporary_store = TemporaryStore::new(
291            store,
292            input_objects,
293            vec![],
294            tx_context.borrow().digest(),
295            protocol_config,
296            0,
297        );
298        let mut gas_charger = GasCharger::new_unmetered(tx_context.borrow().digest());
299        programmable_transactions::execution::execute::<execution_mode::Genesis>(
300            protocol_config,
301            metrics,
302            move_vm,
303            &mut temporary_store,
304            store.as_backing_package_store(),
305            tx_context,
306            &mut gas_charger,
307            pt,
308            &mut None,
309        )
310        .map_err(|(e, _)| e)?;
311        temporary_store.update_object_version_and_prev_tx();
312        Ok(temporary_store.into_inner())
313    }
314
315    #[instrument(name = "tx_execute", level = "debug", skip_all)]
316    fn execute_transaction<Mode: ExecutionMode>(
317        store: &dyn BackingStore,
318        temporary_store: &mut TemporaryStore<'_>,
319        transaction_kind: TransactionKind,
320        gas_charger: &mut GasCharger,
321        tx_ctx: Rc<RefCell<TxContext>>,
322        move_vm: &Arc<MoveVM>,
323        protocol_config: &ProtocolConfig,
324        metrics: Arc<LimitsMetrics>,
325        enable_expensive_checks: bool,
326        execution_params: ExecutionOrEarlyError,
327        trace_builder_opt: &mut Option<MoveTraceBuilder>,
328    ) -> (
329        GasCostSummary,
330        Result<Mode::ExecutionResults, ExecutionError>,
331        Vec<ExecutionTiming>,
332    ) {
333        gas_charger.smash_gas(temporary_store);
334
335        // At this point no charges have been applied yet
336        debug_assert!(
337            gas_charger.no_charges(),
338            "No gas charges must be applied yet"
339        );
340
341        let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_));
342        let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
343        let digest = tx_ctx.borrow().digest();
344
345        // We must charge object read here during transaction execution, because if this fails
346        // we must still ensure an effect is committed and all objects versions incremented
347        let result = gas_charger.charge_input_objects(temporary_store);
348
349        let result: ResultWithTimings<Mode::ExecutionResults, ExecutionError> =
350            result.map_err(|e| (e, vec![])).and_then(
351                |()| -> ResultWithTimings<Mode::ExecutionResults, ExecutionError> {
352                    let mut execution_result: ResultWithTimings<
353                        Mode::ExecutionResults,
354                        ExecutionError,
355                    > = match execution_params {
356                        ExecutionOrEarlyError::Err(early_execution_error) => {
357                            Err((ExecutionError::new(early_execution_error, None), vec![]))
358                        }
359                        ExecutionOrEarlyError::Ok(()) => execution_loop::<Mode>(
360                            store,
361                            temporary_store,
362                            transaction_kind,
363                            tx_ctx,
364                            move_vm,
365                            gas_charger,
366                            protocol_config,
367                            metrics.clone(),
368                            trace_builder_opt,
369                        ),
370                    };
371
372                    let meter_check = check_meter_limit(
373                        temporary_store,
374                        gas_charger,
375                        protocol_config,
376                        metrics.clone(),
377                    );
378                    if let Err(e) = meter_check {
379                        execution_result = Err((e, vec![]));
380                    }
381
382                    if execution_result.is_ok() {
383                        let gas_check = check_written_objects_limit::<Mode>(
384                            temporary_store,
385                            gas_charger,
386                            protocol_config,
387                            metrics,
388                        );
389                        if let Err(e) = gas_check {
390                            execution_result = Err((e, vec![]));
391                        }
392                    }
393
394                    execution_result
395                },
396            );
397
398        let (mut result, timings) = match result {
399            Ok((r, t)) => (Ok(r), t),
400            Err((e, t)) => (Err(e), t),
401        };
402
403        let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
404        // For advance epoch transaction, we need to provide epoch rewards and rebates as extra
405        // information provided to check_sui_conserved, because we mint rewards, and burn
406        // the rebates. We also need to pass in the unmetered_storage_rebate because storage
407        // rebate is not reflected in the storage_rebate of gas summary. This is a bit confusing.
408        // We could probably clean up the code a bit.
409        // Put all the storage rebate accumulated in the system transaction
410        // to the 0x5 object so that it's not lost.
411        temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
412
413        if let Err(e) = run_conservation_checks::<Mode>(
414            temporary_store,
415            gas_charger,
416            digest,
417            move_vm,
418            protocol_config.simple_conservation_checks(),
419            enable_expensive_checks,
420            &cost_summary,
421            is_genesis_tx,
422            advance_epoch_gas_summary,
423        ) {
424            // FIXME: we cannot fail the transaction if this is an epoch change transaction.
425            result = Err(e);
426        }
427
428        (cost_summary, result, timings)
429    }
430
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_digest: TransactionDigest,
436        move_vm: &Arc<MoveVM>,
437        simple_conservation_checks: bool,
438        enable_expensive_checks: bool,
439        cost_summary: &GasCostSummary,
440        is_genesis_tx: bool,
441        advance_epoch_gas_summary: Option<(u64, u64)>,
442    ) -> Result<(), ExecutionError> {
443        let mut result: std::result::Result<(), sui_types::error::ExecutionError> = Ok(());
444        if !is_genesis_tx && !Mode::skip_conservation_checks() {
445            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
446            let conservation_result = {
447                temporary_store
448                    .check_sui_conserved(simple_conservation_checks, cost_summary)
449                    .and_then(|()| {
450                        if enable_expensive_checks {
451                            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
452                            let mut layout_resolver =
453                                TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
454                            temporary_store.check_sui_conserved_expensive(
455                                cost_summary,
456                                advance_epoch_gas_summary,
457                                &mut layout_resolver,
458                            )
459                        } else {
460                            Ok(())
461                        }
462                    })
463            };
464            if let Err(conservation_err) = conservation_result {
465                // conservation violated. try to avoid panic by dumping all writes, charging for gas, re-checking
466                // conservation, and surfacing an aborted transaction with an invariant violation if all of that works
467                result = Err(conservation_err);
468                gas_charger.reset(temporary_store);
469                gas_charger.charge_gas(temporary_store, &mut result);
470                // check conservation once more
471                if let Err(recovery_err) = {
472                    temporary_store
473                        .check_sui_conserved(simple_conservation_checks, cost_summary)
474                        .and_then(|()| {
475                            if enable_expensive_checks {
476                                // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
477                                let mut layout_resolver =
478                                    TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
479                                temporary_store.check_sui_conserved_expensive(
480                                    cost_summary,
481                                    advance_epoch_gas_summary,
482                                    &mut layout_resolver,
483                                )
484                            } else {
485                                Ok(())
486                            }
487                        })
488                } {
489                    // if we still fail, it's a problem with gas
490                    // charging that happens even in the "aborted" case--no other option but panic.
491                    // we will create or destroy SUI otherwise
492                    panic!(
493                        "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
494                        tx_digest,
495                        recovery_err,
496                        gas_charger.summary()
497                    )
498                }
499            }
500        } // else, we're in the genesis transaction which mints the SUI supply, and hence does not satisfy SUI conservation, or
501        // we're in the non-production dev inspect mode which allows us to violate conservation
502        result
503    }
504
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 limit where we terminate
517        match check_limit_by_meter!(
518            !gas_charger.is_unmetered(),
519            effects_estimated_size,
520            protocol_config.max_serialized_tx_effects_size_bytes(),
521            protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
522            metrics.excessive_estimated_effects_size
523        ) {
524            LimitThresholdCrossed::None => Ok(()),
525            LimitThresholdCrossed::Soft(_, limit) => {
526                warn!(
527                    effects_estimated_size = effects_estimated_size,
528                    soft_limit = limit,
529                    "Estimated transaction effects size crossed soft limit",
530                );
531                Ok(())
532            }
533            LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
534                ExecutionErrorKind::EffectsTooLarge {
535                    current_size: effects_estimated_size as u64,
536                    max_size: lim as u64,
537                },
538                "Transaction effects are too large",
539            )),
540        }
541    }
542
543    #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
544    fn check_written_objects_limit<Mode: ExecutionMode>(
545        temporary_store: &mut TemporaryStore<'_>,
546        gas_charger: &mut GasCharger,
547        protocol_config: &ProtocolConfig,
548        metrics: Arc<LimitsMetrics>,
549    ) -> Result<(), ExecutionError> {
550        if let (Some(normal_lim), Some(system_lim)) = (
551            protocol_config.max_size_written_objects_as_option(),
552            protocol_config.max_size_written_objects_system_tx_as_option(),
553        ) {
554            let written_objects_size = temporary_store.written_objects_size();
555
556            match check_limit_by_meter!(
557                !gas_charger.is_unmetered(),
558                written_objects_size,
559                normal_lim,
560                system_lim,
561                metrics.excessive_written_objects_size
562            ) {
563                LimitThresholdCrossed::None => (),
564                LimitThresholdCrossed::Soft(_, limit) => {
565                    warn!(
566                        written_objects_size = written_objects_size,
567                        soft_limit = limit,
568                        "Written objects size crossed soft limit",
569                    )
570                }
571                LimitThresholdCrossed::Hard(_, lim) => {
572                    return Err(ExecutionError::new_with_source(
573                        ExecutionErrorKind::WrittenObjectsTooLarge {
574                            current_size: written_objects_size as u64,
575                            max_size: lim as u64,
576                        },
577                        "Written objects size crossed hard limit",
578                    ));
579                }
580            };
581        }
582
583        Ok(())
584    }
585
586    #[instrument(level = "debug", skip_all)]
587    fn execution_loop<Mode: ExecutionMode>(
588        store: &dyn BackingStore,
589        temporary_store: &mut TemporaryStore<'_>,
590        transaction_kind: TransactionKind,
591        tx_ctx: Rc<RefCell<TxContext>>,
592        move_vm: &Arc<MoveVM>,
593        gas_charger: &mut GasCharger,
594        protocol_config: &ProtocolConfig,
595        metrics: Arc<LimitsMetrics>,
596        trace_builder_opt: &mut Option<MoveTraceBuilder>,
597    ) -> ResultWithTimings<Mode::ExecutionResults, ExecutionError> {
598        let result = match transaction_kind {
599            TransactionKind::ChangeEpoch(change_epoch) => {
600                let builder = ProgrammableTransactionBuilder::new();
601                advance_epoch(
602                    builder,
603                    change_epoch,
604                    temporary_store,
605                    store,
606                    tx_ctx,
607                    move_vm,
608                    gas_charger,
609                    protocol_config,
610                    metrics,
611                    trace_builder_opt,
612                )
613                .map_err(|e| (e, vec![]))?;
614                Ok((Mode::empty_results(), vec![]))
615            }
616            TransactionKind::Genesis(GenesisTransaction { objects }) => {
617                if tx_ctx.borrow().epoch() != 0 {
618                    panic!("BUG: Genesis Transactions can only be executed in epoch 0");
619                }
620
621                for genesis_object in objects {
622                    match genesis_object {
623                        sui_types::transaction::GenesisObject::RawObject { data, owner } => {
624                            let object = ObjectInner {
625                                data,
626                                owner,
627                                previous_transaction: tx_ctx.borrow().digest(),
628                                storage_rebate: 0,
629                            };
630                            temporary_store.create_object(object.into());
631                        }
632                    }
633                }
634                Ok((Mode::empty_results(), vec![]))
635            }
636            TransactionKind::ConsensusCommitPrologue(prologue) => {
637                setup_consensus_commit(
638                    prologue.commit_timestamp_ms,
639                    temporary_store,
640                    store,
641                    tx_ctx,
642                    move_vm,
643                    gas_charger,
644                    protocol_config,
645                    metrics,
646                    trace_builder_opt,
647                )
648                .expect("ConsensusCommitPrologue cannot fail");
649                Ok((Mode::empty_results(), vec![]))
650            }
651            TransactionKind::ConsensusCommitPrologueV2(prologue) => {
652                setup_consensus_commit(
653                    prologue.commit_timestamp_ms,
654                    temporary_store,
655                    store,
656                    tx_ctx,
657                    move_vm,
658                    gas_charger,
659                    protocol_config,
660                    metrics,
661                    trace_builder_opt,
662                )
663                .expect("ConsensusCommitPrologueV2 cannot fail");
664                Ok((Mode::empty_results(), vec![]))
665            }
666            TransactionKind::ConsensusCommitPrologueV3(prologue) => {
667                setup_consensus_commit(
668                    prologue.commit_timestamp_ms,
669                    temporary_store,
670                    store,
671                    tx_ctx,
672                    move_vm,
673                    gas_charger,
674                    protocol_config,
675                    metrics,
676                    trace_builder_opt,
677                )
678                .expect("ConsensusCommitPrologueV3 cannot fail");
679                Ok((Mode::empty_results(), vec![]))
680            }
681            TransactionKind::ConsensusCommitPrologueV4(prologue) => {
682                setup_consensus_commit(
683                    prologue.commit_timestamp_ms,
684                    temporary_store,
685                    store,
686                    tx_ctx,
687                    move_vm,
688                    gas_charger,
689                    protocol_config,
690                    metrics,
691                    trace_builder_opt,
692                )
693                .expect("ConsensusCommitPrologue cannot fail");
694                Ok((Mode::empty_results(), vec![]))
695            }
696            TransactionKind::ProgrammableTransaction(pt) => {
697                programmable_transactions::execution::execute::<Mode>(
698                    protocol_config,
699                    metrics,
700                    move_vm,
701                    temporary_store,
702                    store.as_backing_package_store(),
703                    tx_ctx,
704                    gas_charger,
705                    pt,
706                    trace_builder_opt,
707                )
708            }
709            TransactionKind::ProgrammableSystemTransaction(pt) => {
710                programmable_transactions::execution::execute::<execution_mode::System>(
711                    protocol_config,
712                    metrics,
713                    move_vm,
714                    temporary_store,
715                    store.as_backing_package_store(),
716                    tx_ctx,
717                    gas_charger,
718                    pt,
719                    trace_builder_opt,
720                )?;
721                Ok((Mode::empty_results(), vec![]))
722            }
723            TransactionKind::EndOfEpochTransaction(txns) => {
724                let mut builder = ProgrammableTransactionBuilder::new();
725                let len = txns.len();
726                for (i, tx) in txns.into_iter().enumerate() {
727                    match tx {
728                        EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
729                            assert_eq!(i, len - 1);
730                            advance_epoch(
731                                builder,
732                                change_epoch,
733                                temporary_store,
734                                store,
735                                tx_ctx,
736                                move_vm,
737                                gas_charger,
738                                protocol_config,
739                                metrics,
740                                trace_builder_opt,
741                            )
742                            .map_err(|e| (e, vec![]))?;
743                            return Ok((Mode::empty_results(), vec![]));
744                        }
745                        EndOfEpochTransactionKind::AuthenticatorStateCreate => {
746                            assert!(protocol_config.enable_jwk_consensus_updates());
747                            builder = setup_authenticator_state_create(builder);
748                        }
749                        EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
750                            assert!(protocol_config.enable_jwk_consensus_updates());
751
752                            // TODO: it would be nice if a failure of this function didn't cause
753                            // safe mode.
754                            builder = setup_authenticator_state_expire(builder, expire);
755                        }
756                        EndOfEpochTransactionKind::RandomnessStateCreate => {
757                            assert!(protocol_config.random_beacon());
758                            builder = setup_randomness_state_create(builder);
759                        }
760                        EndOfEpochTransactionKind::DenyListStateCreate => {
761                            assert!(protocol_config.enable_coin_deny_list_v1());
762                            builder = setup_coin_deny_list_state_create(builder);
763                        }
764                        EndOfEpochTransactionKind::BridgeStateCreate(chain_id) => {
765                            assert!(protocol_config.enable_bridge());
766                            builder = setup_bridge_create(builder, chain_id)
767                        }
768                        EndOfEpochTransactionKind::BridgeCommitteeInit(bridge_shared_version) => {
769                            assert!(protocol_config.enable_bridge());
770                            assert!(protocol_config.should_try_to_finalize_bridge_committee());
771                            builder = setup_bridge_committee_update(builder, bridge_shared_version)
772                        }
773                        EndOfEpochTransactionKind::StoreExecutionTimeObservations(estimates) => {
774                            if let PerObjectCongestionControlMode::ExecutionTimeEstimate(params) =
775                                protocol_config.per_object_congestion_control_mode()
776                            {
777                                if let Some(chunk_size) = params.observations_chunk_size {
778                                    builder = setup_store_execution_time_estimates_v2(
779                                        builder,
780                                        estimates,
781                                        chunk_size as usize,
782                                    );
783                                } else {
784                                    builder =
785                                        setup_store_execution_time_estimates(builder, estimates);
786                                }
787                            }
788                        }
789                        EndOfEpochTransactionKind::AccumulatorRootCreate => {
790                            assert!(protocol_config.create_root_accumulator_object());
791                            builder = setup_accumulator_root_create(builder);
792                        }
793                        EndOfEpochTransactionKind::CoinRegistryCreate => {
794                            assert!(protocol_config.enable_coin_registry());
795                            builder = setup_coin_registry_create(builder);
796                        }
797                        EndOfEpochTransactionKind::DisplayRegistryCreate => {
798                            assert!(protocol_config.enable_display_registry());
799                            builder = setup_display_registry_create(builder);
800                        }
801                    }
802                }
803                unreachable!(
804                    "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
805                )
806            }
807            TransactionKind::AuthenticatorStateUpdate(auth_state_update) => {
808                setup_authenticator_state_update(
809                    auth_state_update,
810                    temporary_store,
811                    store,
812                    tx_ctx,
813                    move_vm,
814                    gas_charger,
815                    protocol_config,
816                    metrics,
817                    trace_builder_opt,
818                )
819                .map_err(|e| (e, vec![]))?;
820                Ok((Mode::empty_results(), vec![]))
821            }
822            TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
823                setup_randomness_state_update(
824                    randomness_state_update,
825                    temporary_store,
826                    store,
827                    tx_ctx,
828                    move_vm,
829                    gas_charger,
830                    protocol_config,
831                    metrics,
832                    trace_builder_opt,
833                )
834                .map_err(|e| (e, vec![]))?;
835                Ok((Mode::empty_results(), vec![]))
836            }
837        }?;
838        temporary_store
839            .check_execution_results_consistency()
840            .map_err(|e| (e, vec![]))?;
841        Ok(result)
842    }
843
844    fn mint_epoch_rewards_in_pt(
845        builder: &mut ProgrammableTransactionBuilder,
846        params: &AdvanceEpochParams,
847    ) -> (Argument, Argument) {
848        // Create storage rewards.
849        let storage_charge_arg = builder
850            .input(CallArg::Pure(
851                bcs::to_bytes(&params.storage_charge).unwrap(),
852            ))
853            .unwrap();
854        let storage_rewards = builder.programmable_move_call(
855            SUI_FRAMEWORK_PACKAGE_ID,
856            BALANCE_MODULE_NAME.to_owned(),
857            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
858            vec![GAS::type_tag()],
859            vec![storage_charge_arg],
860        );
861
862        // Create computation rewards.
863        let computation_charge_arg = builder
864            .input(CallArg::Pure(
865                bcs::to_bytes(&params.computation_charge).unwrap(),
866            ))
867            .unwrap();
868        let computation_rewards = builder.programmable_move_call(
869            SUI_FRAMEWORK_PACKAGE_ID,
870            BALANCE_MODULE_NAME.to_owned(),
871            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
872            vec![GAS::type_tag()],
873            vec![computation_charge_arg],
874        );
875        (storage_rewards, computation_rewards)
876    }
877
878    pub fn construct_advance_epoch_pt(
879        mut builder: ProgrammableTransactionBuilder,
880        params: &AdvanceEpochParams,
881    ) -> Result<ProgrammableTransaction, ExecutionError> {
882        // Step 1: Create storage and computation rewards.
883        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
884
885        // Step 2: Advance the epoch.
886        let mut arguments = vec![storage_rewards, computation_rewards];
887        let call_arg_arguments = vec![
888            CallArg::SUI_SYSTEM_MUT,
889            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
890            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
891            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
892            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
893            CallArg::Pure(bcs::to_bytes(&params.storage_fund_reinvest_rate).unwrap()),
894            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()),
895            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()),
896        ]
897        .into_iter()
898        .map(|a| builder.input(a))
899        .collect::<Result<_, _>>();
900
901        assert_invariant!(
902            call_arg_arguments.is_ok(),
903            "Unable to generate args for advance_epoch transaction!"
904        );
905
906        arguments.append(&mut call_arg_arguments.unwrap());
907
908        info!("Call arguments to advance_epoch transaction: {:?}", params);
909
910        let storage_rebates = builder.programmable_move_call(
911            SUI_SYSTEM_PACKAGE_ID,
912            SUI_SYSTEM_MODULE_NAME.to_owned(),
913            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
914            vec![],
915            arguments,
916        );
917
918        // Step 3: Destroy the storage rebates.
919        builder.programmable_move_call(
920            SUI_FRAMEWORK_PACKAGE_ID,
921            BALANCE_MODULE_NAME.to_owned(),
922            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
923            vec![GAS::type_tag()],
924            vec![storage_rebates],
925        );
926        Ok(builder.finish())
927    }
928
929    pub fn construct_advance_epoch_safe_mode_pt(
930        params: &AdvanceEpochParams,
931        protocol_config: &ProtocolConfig,
932    ) -> Result<ProgrammableTransaction, ExecutionError> {
933        let mut builder = ProgrammableTransactionBuilder::new();
934        // Step 1: Create storage and computation rewards.
935        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
936
937        // Step 2: Advance the epoch.
938        let mut arguments = vec![storage_rewards, computation_rewards];
939
940        let mut args = vec![
941            CallArg::SUI_SYSTEM_MUT,
942            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
943            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
944            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
945            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
946        ];
947
948        if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
949            args.push(CallArg::Pure(
950                bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap(),
951            ));
952        }
953
954        let call_arg_arguments = args
955            .into_iter()
956            .map(|a| builder.input(a))
957            .collect::<Result<_, _>>();
958
959        assert_invariant!(
960            call_arg_arguments.is_ok(),
961            "Unable to generate args for advance_epoch transaction!"
962        );
963
964        arguments.append(&mut call_arg_arguments.unwrap());
965
966        info!("Call arguments to advance_epoch transaction: {:?}", params);
967
968        builder.programmable_move_call(
969            SUI_SYSTEM_PACKAGE_ID,
970            SUI_SYSTEM_MODULE_NAME.to_owned(),
971            ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
972            vec![],
973            arguments,
974        );
975
976        Ok(builder.finish())
977    }
978
979    fn advance_epoch(
980        builder: ProgrammableTransactionBuilder,
981        change_epoch: ChangeEpoch,
982        temporary_store: &mut TemporaryStore<'_>,
983        store: &dyn BackingStore,
984        tx_ctx: Rc<RefCell<TxContext>>,
985        move_vm: &Arc<MoveVM>,
986        gas_charger: &mut GasCharger,
987        protocol_config: &ProtocolConfig,
988        metrics: Arc<LimitsMetrics>,
989        trace_builder_opt: &mut Option<MoveTraceBuilder>,
990    ) -> Result<(), ExecutionError> {
991        let params = AdvanceEpochParams {
992            epoch: change_epoch.epoch,
993            next_protocol_version: change_epoch.protocol_version,
994            storage_charge: change_epoch.storage_charge,
995            computation_charge: change_epoch.computation_charge,
996            storage_rebate: change_epoch.storage_rebate,
997            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
998            storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
999            reward_slashing_rate: protocol_config.reward_slashing_rate(),
1000            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
1001        };
1002        let advance_epoch_pt = construct_advance_epoch_pt(builder, &params)?;
1003        let result = programmable_transactions::execution::execute::<execution_mode::System>(
1004            protocol_config,
1005            metrics.clone(),
1006            move_vm,
1007            temporary_store,
1008            store.as_backing_package_store(),
1009            tx_ctx.clone(),
1010            gas_charger,
1011            advance_epoch_pt,
1012            trace_builder_opt,
1013        );
1014
1015        #[cfg(msim)]
1016        let result = maybe_modify_result(result, change_epoch.epoch);
1017
1018        if let Err(err) = &result {
1019            tracing::error!(
1020                "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
1021                err.0,
1022                temporary_store.objects(),
1023                change_epoch,
1024            );
1025            temporary_store.drop_writes();
1026            // Must reset the storage rebate since we are re-executing.
1027            gas_charger.reset_storage_cost_and_rebate();
1028
1029            if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
1030                temporary_store.advance_epoch_safe_mode(&params, protocol_config);
1031            } else {
1032                let advance_epoch_safe_mode_pt =
1033                    construct_advance_epoch_safe_mode_pt(&params, protocol_config)?;
1034                programmable_transactions::execution::execute::<execution_mode::System>(
1035                    protocol_config,
1036                    metrics.clone(),
1037                    move_vm,
1038                    temporary_store,
1039                    store.as_backing_package_store(),
1040                    tx_ctx.clone(),
1041                    gas_charger,
1042                    advance_epoch_safe_mode_pt,
1043                    trace_builder_opt,
1044                )
1045                .map_err(|(e, _)| e)
1046                .expect("Advance epoch with safe mode must succeed");
1047            }
1048        }
1049
1050        if protocol_config.fresh_vm_on_framework_upgrade() {
1051            let new_vm = new_move_vm(
1052                all_natives(/* silent */ true, protocol_config),
1053                protocol_config,
1054            )
1055            .expect("Failed to create new MoveVM");
1056            process_system_packages(
1057                change_epoch,
1058                temporary_store,
1059                store,
1060                tx_ctx,
1061                &new_vm,
1062                gas_charger,
1063                protocol_config,
1064                metrics,
1065                trace_builder_opt,
1066            );
1067        } else {
1068            process_system_packages(
1069                change_epoch,
1070                temporary_store,
1071                store,
1072                tx_ctx,
1073                move_vm,
1074                gas_charger,
1075                protocol_config,
1076                metrics,
1077                trace_builder_opt,
1078            );
1079        }
1080        Ok(())
1081    }
1082
1083    fn process_system_packages(
1084        change_epoch: ChangeEpoch,
1085        temporary_store: &mut TemporaryStore<'_>,
1086        store: &dyn BackingStore,
1087        tx_ctx: Rc<RefCell<TxContext>>,
1088        move_vm: &MoveVM,
1089        gas_charger: &mut GasCharger,
1090        protocol_config: &ProtocolConfig,
1091        metrics: Arc<LimitsMetrics>,
1092        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1093    ) {
1094        let digest = tx_ctx.borrow().digest();
1095        let binary_config = protocol_config.binary_config(None);
1096        for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
1097            let deserialized_modules: Vec<_> = modules
1098                .iter()
1099                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
1100                .collect();
1101
1102            if version == OBJECT_START_VERSION {
1103                let package_id = deserialized_modules.first().unwrap().address();
1104                info!("adding new system package {package_id}");
1105
1106                let publish_pt = {
1107                    let mut b = ProgrammableTransactionBuilder::new();
1108                    b.command(Command::Publish(modules, dependencies));
1109                    b.finish()
1110                };
1111
1112                programmable_transactions::execution::execute::<execution_mode::System>(
1113                    protocol_config,
1114                    metrics.clone(),
1115                    move_vm,
1116                    temporary_store,
1117                    store.as_backing_package_store(),
1118                    tx_ctx.clone(),
1119                    gas_charger,
1120                    publish_pt,
1121                    trace_builder_opt,
1122                )
1123                .map_err(|(e, _)| e)
1124                .expect("System Package Publish must succeed");
1125            } else {
1126                let mut new_package = Object::new_system_package(
1127                    &deserialized_modules,
1128                    version,
1129                    dependencies,
1130                    digest,
1131                );
1132
1133                info!(
1134                    "upgraded system package {:?}",
1135                    new_package.compute_object_reference()
1136                );
1137
1138                // Decrement the version before writing the package so that the store can record the
1139                // version growing by one in the effects.
1140                new_package
1141                    .data
1142                    .try_as_package_mut()
1143                    .unwrap()
1144                    .decrement_version();
1145
1146                // upgrade of a previously existing framework module
1147                temporary_store.upgrade_system_package(new_package);
1148            }
1149        }
1150    }
1151
1152    /// Perform metadata updates in preparation for the transactions in the upcoming checkpoint:
1153    ///
1154    /// - Set the timestamp for the `Clock` shared object from the timestamp in the header from
1155    ///   consensus.
1156    fn setup_consensus_commit(
1157        consensus_commit_timestamp_ms: CheckpointTimestamp,
1158        temporary_store: &mut TemporaryStore<'_>,
1159        store: &dyn BackingStore,
1160        tx_ctx: Rc<RefCell<TxContext>>,
1161        move_vm: &Arc<MoveVM>,
1162        gas_charger: &mut GasCharger,
1163        protocol_config: &ProtocolConfig,
1164        metrics: Arc<LimitsMetrics>,
1165        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1166    ) -> Result<(), ExecutionError> {
1167        let pt = {
1168            let mut builder = ProgrammableTransactionBuilder::new();
1169            let res = builder.move_call(
1170                SUI_FRAMEWORK_ADDRESS.into(),
1171                CLOCK_MODULE_NAME.to_owned(),
1172                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
1173                vec![],
1174                vec![
1175                    CallArg::CLOCK_MUT,
1176                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
1177                ],
1178            );
1179            assert_invariant!(
1180                res.is_ok(),
1181                "Unable to generate consensus_commit_prologue transaction!"
1182            );
1183            builder.finish()
1184        };
1185        programmable_transactions::execution::execute::<execution_mode::System>(
1186            protocol_config,
1187            metrics,
1188            move_vm,
1189            temporary_store,
1190            store.as_backing_package_store(),
1191            tx_ctx,
1192            gas_charger,
1193            pt,
1194            trace_builder_opt,
1195        )
1196        .map_err(|(e, _)| e)?;
1197        Ok(())
1198    }
1199
1200    fn setup_authenticator_state_create(
1201        mut builder: ProgrammableTransactionBuilder,
1202    ) -> ProgrammableTransactionBuilder {
1203        builder
1204            .move_call(
1205                SUI_FRAMEWORK_ADDRESS.into(),
1206                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1207                AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1208                vec![],
1209                vec![],
1210            )
1211            .expect("Unable to generate authenticator_state_create transaction!");
1212        builder
1213    }
1214
1215    fn setup_randomness_state_create(
1216        mut builder: ProgrammableTransactionBuilder,
1217    ) -> ProgrammableTransactionBuilder {
1218        builder
1219            .move_call(
1220                SUI_FRAMEWORK_ADDRESS.into(),
1221                RANDOMNESS_MODULE_NAME.to_owned(),
1222                RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
1223                vec![],
1224                vec![],
1225            )
1226            .expect("Unable to generate randomness_state_create transaction!");
1227        builder
1228    }
1229
1230    fn setup_bridge_create(
1231        mut builder: ProgrammableTransactionBuilder,
1232        chain_id: ChainIdentifier,
1233    ) -> ProgrammableTransactionBuilder {
1234        let bridge_uid = builder
1235            .input(CallArg::Pure(UID::new(SUI_BRIDGE_OBJECT_ID).to_bcs_bytes()))
1236            .expect("Unable to create Bridge object UID!");
1237
1238        let bridge_chain_id = if chain_id == get_mainnet_chain_identifier() {
1239            BridgeChainId::SuiMainnet as u8
1240        } else if chain_id == get_testnet_chain_identifier() {
1241            BridgeChainId::SuiTestnet as u8
1242        } else {
1243            // How do we distinguish devnet from other test envs?
1244            BridgeChainId::SuiCustom as u8
1245        };
1246
1247        let bridge_chain_id = builder.pure(bridge_chain_id).unwrap();
1248        builder.programmable_move_call(
1249            BRIDGE_ADDRESS.into(),
1250            BRIDGE_MODULE_NAME.to_owned(),
1251            BRIDGE_CREATE_FUNCTION_NAME.to_owned(),
1252            vec![],
1253            vec![bridge_uid, bridge_chain_id],
1254        );
1255        builder
1256    }
1257
1258    fn setup_bridge_committee_update(
1259        mut builder: ProgrammableTransactionBuilder,
1260        bridge_shared_version: SequenceNumber,
1261    ) -> ProgrammableTransactionBuilder {
1262        let bridge = builder
1263            .obj(ObjectArg::SharedObject {
1264                id: SUI_BRIDGE_OBJECT_ID,
1265                initial_shared_version: bridge_shared_version,
1266                mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1267            })
1268            .expect("Unable to create Bridge object arg!");
1269        let system_state = builder
1270            .obj(ObjectArg::SUI_SYSTEM_MUT)
1271            .expect("Unable to create System State object arg!");
1272
1273        let voting_power = builder.programmable_move_call(
1274            SUI_SYSTEM_PACKAGE_ID,
1275            SUI_SYSTEM_MODULE_NAME.to_owned(),
1276            ident_str!("validator_voting_powers").to_owned(),
1277            vec![],
1278            vec![system_state],
1279        );
1280
1281        // Hardcoding min stake participation to 75.00%
1282        // TODO: We need to set a correct value or make this configurable.
1283        let min_stake_participation_percentage = builder
1284            .input(CallArg::Pure(
1285                bcs::to_bytes(&BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER).unwrap(),
1286            ))
1287            .unwrap();
1288
1289        builder.programmable_move_call(
1290            BRIDGE_ADDRESS.into(),
1291            BRIDGE_MODULE_NAME.to_owned(),
1292            BRIDGE_INIT_COMMITTEE_FUNCTION_NAME.to_owned(),
1293            vec![],
1294            vec![bridge, voting_power, min_stake_participation_percentage],
1295        );
1296        builder
1297    }
1298
1299    fn setup_authenticator_state_update(
1300        update: AuthenticatorStateUpdate,
1301        temporary_store: &mut TemporaryStore<'_>,
1302        store: &dyn BackingStore,
1303        tx_ctx: Rc<RefCell<TxContext>>,
1304        move_vm: &Arc<MoveVM>,
1305        gas_charger: &mut GasCharger,
1306        protocol_config: &ProtocolConfig,
1307        metrics: Arc<LimitsMetrics>,
1308        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1309    ) -> Result<(), ExecutionError> {
1310        let pt = {
1311            let mut builder = ProgrammableTransactionBuilder::new();
1312            let res = builder.move_call(
1313                SUI_FRAMEWORK_ADDRESS.into(),
1314                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1315                AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1316                vec![],
1317                vec![
1318                    CallArg::Object(ObjectArg::SharedObject {
1319                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1320                        initial_shared_version: update.authenticator_obj_initial_shared_version,
1321                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1322                    }),
1323                    CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1324                ],
1325            );
1326            assert_invariant!(
1327                res.is_ok(),
1328                "Unable to generate authenticator_state_update transaction!"
1329            );
1330            builder.finish()
1331        };
1332        programmable_transactions::execution::execute::<execution_mode::System>(
1333            protocol_config,
1334            metrics,
1335            move_vm,
1336            temporary_store,
1337            store.as_backing_package_store(),
1338            tx_ctx,
1339            gas_charger,
1340            pt,
1341            trace_builder_opt,
1342        )
1343        .map_err(|(e, _)| e)?;
1344        Ok(())
1345    }
1346
1347    fn setup_authenticator_state_expire(
1348        mut builder: ProgrammableTransactionBuilder,
1349        expire: AuthenticatorStateExpire,
1350    ) -> ProgrammableTransactionBuilder {
1351        builder
1352            .move_call(
1353                SUI_FRAMEWORK_ADDRESS.into(),
1354                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1355                AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1356                vec![],
1357                vec![
1358                    CallArg::Object(ObjectArg::SharedObject {
1359                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1360                        initial_shared_version: expire.authenticator_obj_initial_shared_version,
1361                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1362                    }),
1363                    CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1364                ],
1365            )
1366            .expect("Unable to generate authenticator_state_expire transaction!");
1367        builder
1368    }
1369
1370    fn setup_randomness_state_update(
1371        update: RandomnessStateUpdate,
1372        temporary_store: &mut TemporaryStore<'_>,
1373        store: &dyn BackingStore,
1374        tx_ctx: Rc<RefCell<TxContext>>,
1375        move_vm: &Arc<MoveVM>,
1376        gas_charger: &mut GasCharger,
1377        protocol_config: &ProtocolConfig,
1378        metrics: Arc<LimitsMetrics>,
1379        trace_builder_opt: &mut Option<MoveTraceBuilder>,
1380    ) -> Result<(), ExecutionError> {
1381        let pt = {
1382            let mut builder = ProgrammableTransactionBuilder::new();
1383            let res = builder.move_call(
1384                SUI_FRAMEWORK_ADDRESS.into(),
1385                RANDOMNESS_MODULE_NAME.to_owned(),
1386                RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1387                vec![],
1388                vec![
1389                    CallArg::Object(ObjectArg::SharedObject {
1390                        id: SUI_RANDOMNESS_STATE_OBJECT_ID,
1391                        initial_shared_version: update.randomness_obj_initial_shared_version,
1392                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1393                    }),
1394                    CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1395                    CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1396                ],
1397            );
1398            assert_invariant!(
1399                res.is_ok(),
1400                "Unable to generate randomness_state_update transaction!"
1401            );
1402            builder.finish()
1403        };
1404        programmable_transactions::execution::execute::<execution_mode::System>(
1405            protocol_config,
1406            metrics,
1407            move_vm,
1408            temporary_store,
1409            store.as_backing_package_store(),
1410            tx_ctx,
1411            gas_charger,
1412            pt,
1413            trace_builder_opt,
1414        )
1415        .map_err(|(e, _)| e)?;
1416        Ok(())
1417    }
1418
1419    fn setup_coin_deny_list_state_create(
1420        mut builder: ProgrammableTransactionBuilder,
1421    ) -> ProgrammableTransactionBuilder {
1422        builder
1423            .move_call(
1424                SUI_FRAMEWORK_ADDRESS.into(),
1425                DENY_LIST_MODULE.to_owned(),
1426                DENY_LIST_CREATE_FUNC.to_owned(),
1427                vec![],
1428                vec![],
1429            )
1430            .expect("Unable to generate coin_deny_list_create transaction!");
1431        builder
1432    }
1433
1434    fn setup_store_execution_time_estimates(
1435        mut builder: ProgrammableTransactionBuilder,
1436        estimates: StoredExecutionTimeObservations,
1437    ) -> ProgrammableTransactionBuilder {
1438        let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
1439        // This is stored as a vector<u8> in Move, so we first convert to bytes before again
1440        // serializing inside the call to `pure`.
1441        let estimates_bytes = bcs::to_bytes(&estimates).unwrap();
1442        let estimates_arg = builder.pure(estimates_bytes).unwrap();
1443        builder.programmable_move_call(
1444            SUI_SYSTEM_PACKAGE_ID,
1445            SUI_SYSTEM_MODULE_NAME.to_owned(),
1446            ident_str!("store_execution_time_estimates").to_owned(),
1447            vec![],
1448            vec![system_state, estimates_arg],
1449        );
1450        builder
1451    }
1452
1453    fn setup_store_execution_time_estimates_v2(
1454        mut builder: ProgrammableTransactionBuilder,
1455        estimates: StoredExecutionTimeObservations,
1456        chunk_size: usize,
1457    ) -> ProgrammableTransactionBuilder {
1458        let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
1459
1460        let estimate_chunks = estimates.chunk_observations(chunk_size);
1461
1462        let chunk_bytes: Vec<Vec<u8>> = estimate_chunks
1463            .into_iter()
1464            .map(|chunk| bcs::to_bytes(&chunk).unwrap())
1465            .collect();
1466
1467        let chunks_arg = builder.pure(chunk_bytes).unwrap();
1468
1469        builder.programmable_move_call(
1470            SUI_SYSTEM_PACKAGE_ID,
1471            SUI_SYSTEM_MODULE_NAME.to_owned(),
1472            ident_str!("store_execution_time_estimates_v2").to_owned(),
1473            vec![],
1474            vec![system_state, chunks_arg],
1475        );
1476        builder
1477    }
1478
1479    fn setup_accumulator_root_create(
1480        mut builder: ProgrammableTransactionBuilder,
1481    ) -> ProgrammableTransactionBuilder {
1482        builder
1483            .move_call(
1484                SUI_FRAMEWORK_ADDRESS.into(),
1485                ACCUMULATOR_ROOT_MODULE.to_owned(),
1486                ACCUMULATOR_ROOT_CREATE_FUNC.to_owned(),
1487                vec![],
1488                vec![],
1489            )
1490            .expect("Unable to generate accumulator_root_create transaction!");
1491        builder
1492    }
1493
1494    fn setup_coin_registry_create(
1495        mut builder: ProgrammableTransactionBuilder,
1496    ) -> ProgrammableTransactionBuilder {
1497        builder
1498            .move_call(
1499                SUI_FRAMEWORK_ADDRESS.into(),
1500                ident_str!("coin_registry").to_owned(),
1501                ident_str!("create").to_owned(),
1502                vec![],
1503                vec![],
1504            )
1505            .expect("Unable to generate coin_registry_create transaction!");
1506        builder
1507    }
1508
1509    fn setup_display_registry_create(
1510        mut builder: ProgrammableTransactionBuilder,
1511    ) -> ProgrammableTransactionBuilder {
1512        builder
1513            .move_call(
1514                SUI_FRAMEWORK_ADDRESS.into(),
1515                ident_str!("display_registry").to_owned(),
1516                ident_str!("create").to_owned(),
1517                vec![],
1518                vec![],
1519            )
1520            .expect("Unable to generate display_registry_create transaction!");
1521        builder
1522    }
1523}