Skip to main content

sui_adapter_v2/
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 move_binary_format::CompiledModule;
11    use move_vm_runtime::move_vm::MoveVM;
12    use std::{collections::HashSet, sync::Arc};
13    use sui_types::balance::{
14        BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
15        BALANCE_MODULE_NAME,
16    };
17    use sui_types::execution_params::ExecutionOrEarlyError;
18    use sui_types::gas_coin::GAS;
19    use sui_types::messages_checkpoint::CheckpointTimestamp;
20    use sui_types::metrics::ExecutionMetrics;
21    use sui_types::object::OBJECT_START_VERSION;
22    use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
23    use sui_types::randomness_state::{
24        RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_CREATE_FUNCTION_NAME,
25        RANDOMNESS_STATE_UPDATE_FUNCTION_NAME,
26    };
27    use sui_types::SUI_RANDOMNESS_STATE_OBJECT_ID;
28    use tracing::{info, instrument, trace, warn};
29
30    use crate::programmable_transactions;
31    use crate::type_layout_resolver::TypeLayoutResolver;
32    use crate::{gas_charger::GasCharger, temporary_store::TemporaryStore};
33    use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed, ProtocolConfig};
34    use sui_types::authenticator_state::{
35        AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME, AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME,
36        AUTHENTICATOR_STATE_MODULE_NAME, AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
37    };
38    use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME};
39    use sui_types::committee::EpochId;
40    use sui_types::deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE};
41    use sui_types::effects::TransactionEffects;
42    use sui_types::error::{ExecutionError, ExecutionErrorTrait};
43    use sui_types::execution_status::{ExecutionErrorKind, ExecutionStatus};
44    use sui_types::gas::GasCostSummary;
45    use sui_types::gas::SuiGasStatus;
46    use sui_types::inner_temporary_store::InnerTemporaryStore;
47    use sui_types::storage::BackingStore;
48    #[cfg(msim)]
49    use sui_types::sui_system_state::advance_epoch_result_injection::maybe_modify_result_legacy;
50    use sui_types::sui_system_state::{AdvanceEpochParams, ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME};
51    use sui_types::transaction::{
52        Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, CallArg, ChangeEpoch,
53        Command, EndOfEpochTransactionKind, GenesisTransaction, ObjectArg, ProgrammableTransaction,
54        TransactionKind,
55    };
56    use sui_types::transaction::{CheckedInputObjects, RandomnessStateUpdate};
57    use sui_types::{
58        base_types::{ObjectRef, SuiAddress, TransactionDigest, TxContext},
59        object::{Object, ObjectInner},
60        sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME},
61        SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_FRAMEWORK_PACKAGE_ID,
62        SUI_SYSTEM_PACKAGE_ID,
63    };
64
65    #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
66    pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
67        store: &dyn BackingStore,
68        input_objects: CheckedInputObjects,
69        gas_coins: Vec<ObjectRef>,
70        gas_status: SuiGasStatus,
71        transaction_kind: TransactionKind,
72        transaction_signer: SuiAddress,
73        transaction_digest: TransactionDigest,
74        move_vm: &Arc<MoveVM>,
75        epoch_id: &EpochId,
76        epoch_timestamp_ms: u64,
77        protocol_config: &ProtocolConfig,
78        metrics: Arc<ExecutionMetrics>,
79        enable_expensive_checks: bool,
80        execution_params: ExecutionOrEarlyError,
81    ) -> (
82        InnerTemporaryStore,
83        SuiGasStatus,
84        TransactionEffects,
85        Result<Mode::ExecutionResults, ExecutionError>,
86    ) {
87        let input_objects = input_objects.into_inner();
88        let mutable_inputs = if enable_expensive_checks {
89            input_objects.all_mutable_inputs().keys().copied().collect()
90        } else {
91            HashSet::new()
92        };
93        let shared_object_refs = input_objects.filter_shared_objects();
94        let receiving_objects = transaction_kind.receiving_objects();
95        let mut transaction_dependencies = input_objects.transaction_dependencies();
96
97        let mut temporary_store = TemporaryStore::new(
98            store,
99            input_objects,
100            receiving_objects,
101            transaction_digest,
102            protocol_config,
103        );
104
105        let mut gas_charger =
106            GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
107
108        let mut tx_ctx = TxContext::new_from_components(
109            &transaction_signer,
110            &transaction_digest,
111            epoch_id,
112            epoch_timestamp_ms,
113            // Those values are unused in execution versions before 3 (or latest)
114            1,
115            1,
116            1_000_000,
117            None,
118            protocol_config,
119        );
120
121        let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
122
123        let (gas_cost_summary, execution_result) = execute_transaction::<Mode>(
124            &mut temporary_store,
125            transaction_kind,
126            &mut gas_charger,
127            &mut tx_ctx,
128            move_vm,
129            protocol_config,
130            metrics,
131            enable_expensive_checks,
132            execution_params,
133        );
134
135        let status = if let Err(error) = &execution_result {
136            ExecutionStatus::new_failure(error.to_execution_failure())
137        } else {
138            ExecutionStatus::Success
139        };
140
141        #[skip_checked_arithmetic]
142        trace!(
143            tx_digest = ?transaction_digest,
144            computation_gas_cost = gas_cost_summary.computation_cost,
145            storage_gas_cost = gas_cost_summary.storage_cost,
146            storage_gas_rebate = gas_cost_summary.storage_rebate,
147            "Finished execution of transaction with status {:?}",
148            status
149        );
150
151        // Genesis writes a special digest to indicate that an object was created during
152        // genesis and not written by any normal transaction - remove that from the
153        // dependencies
154        transaction_dependencies.remove(&TransactionDigest::genesis_marker());
155
156        if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
157            temporary_store
158                .check_ownership_invariants(
159                    &transaction_signer,
160                    &mut gas_charger,
161                    &mutable_inputs,
162                    is_epoch_change,
163                )
164                .unwrap()
165        } // else, in dev inspect mode and anything goes--don't check
166
167        let (inner, effects) = temporary_store.into_effects(
168            shared_object_refs,
169            &transaction_digest,
170            transaction_dependencies,
171            gas_cost_summary,
172            status,
173            &mut gas_charger,
174            *epoch_id,
175        );
176
177        (
178            inner,
179            gas_charger.into_gas_status(),
180            effects,
181            execution_result,
182        )
183    }
184
185    pub fn execute_genesis_state_update(
186        store: &dyn BackingStore,
187        protocol_config: &ProtocolConfig,
188        metrics: Arc<ExecutionMetrics>,
189        move_vm: &Arc<MoveVM>,
190        tx_context: &mut TxContext,
191        input_objects: CheckedInputObjects,
192        pt: ProgrammableTransaction,
193    ) -> Result<InnerTemporaryStore, ExecutionError> {
194        let input_objects = input_objects.into_inner();
195        let mut temporary_store = TemporaryStore::new(
196            store,
197            input_objects,
198            vec![],
199            tx_context.digest(),
200            protocol_config,
201        );
202        let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
203        programmable_transactions::execution::execute::<execution_mode::Genesis>(
204            protocol_config,
205            metrics,
206            move_vm,
207            &mut temporary_store,
208            tx_context,
209            &mut gas_charger,
210            pt,
211        )?;
212        temporary_store.update_object_version_and_prev_tx();
213        Ok(temporary_store.into_inner())
214    }
215
216    #[instrument(name = "tx_execute", level = "debug", skip_all)]
217    fn execute_transaction<Mode: ExecutionMode>(
218        temporary_store: &mut TemporaryStore<'_>,
219        transaction_kind: TransactionKind,
220        gas_charger: &mut GasCharger,
221        tx_ctx: &mut TxContext,
222        move_vm: &Arc<MoveVM>,
223        protocol_config: &ProtocolConfig,
224        metrics: Arc<ExecutionMetrics>,
225        enable_expensive_checks: bool,
226        execution_params: ExecutionOrEarlyError,
227    ) -> (
228        GasCostSummary,
229        Result<Mode::ExecutionResults, ExecutionError>,
230    ) {
231        gas_charger.smash_gas(temporary_store);
232
233        // At this point no charges have been applied yet
234        debug_assert!(
235            gas_charger.no_charges(),
236            "No gas charges must be applied yet"
237        );
238
239        let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_));
240        let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
241
242        // We must charge object read here during transaction execution, because if this fails
243        // we must still ensure an effect is committed and all objects versions incremented
244        let result = gas_charger.charge_input_objects(temporary_store);
245        let mut result = result.and_then(|()| {
246            let mut execution_result = match execution_params.into_early_errors() {
247                Some(early_execution_errors) => {
248                    Err(ExecutionError::new(early_execution_errors.head, None))
249                }
250                None => execution_loop::<Mode>(
251                    temporary_store,
252                    transaction_kind,
253                    tx_ctx,
254                    move_vm,
255                    gas_charger,
256                    protocol_config,
257                    metrics.clone(),
258                ),
259            };
260
261            let meter_check = check_meter_limit(
262                temporary_store,
263                gas_charger,
264                protocol_config,
265                metrics.clone(),
266            );
267            if let Err(e) = meter_check {
268                execution_result = Err(e);
269            }
270
271            if execution_result.is_ok() {
272                let gas_check = check_written_objects_limit(
273                    temporary_store,
274                    gas_charger,
275                    protocol_config,
276                    metrics,
277                );
278                if let Err(e) = gas_check {
279                    execution_result = Err(e);
280                }
281            }
282
283            execution_result
284        });
285
286        let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
287        // For advance epoch transaction, we need to provide epoch rewards and rebates as extra
288        // information provided to check_sui_conserved, because we mint rewards, and burn
289        // the rebates. We also need to pass in the unmetered_storage_rebate because storage
290        // rebate is not reflected in the storage_rebate of gas summary. This is a bit confusing.
291        // We could probably clean up the code a bit.
292        // Put all the storage rebate accumulated in the system transaction
293        // to the 0x5 object so that it's not lost.
294        temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
295
296        if let Err(e) = run_conservation_checks::<Mode>(
297            temporary_store,
298            gas_charger,
299            tx_ctx,
300            move_vm,
301            protocol_config.simple_conservation_checks(),
302            enable_expensive_checks,
303            &cost_summary,
304            is_genesis_tx,
305            advance_epoch_gas_summary,
306        ) {
307            // FIXME: we cannot fail the transaction if this is an epoch change transaction.
308            result = Err(e);
309        }
310
311        (cost_summary, result)
312    }
313
314    #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
315    fn run_conservation_checks<Mode: ExecutionMode>(
316        temporary_store: &mut TemporaryStore<'_>,
317        gas_charger: &mut GasCharger,
318        tx_ctx: &mut TxContext,
319        move_vm: &Arc<MoveVM>,
320        simple_conservation_checks: bool,
321        enable_expensive_checks: bool,
322        cost_summary: &GasCostSummary,
323        is_genesis_tx: bool,
324        advance_epoch_gas_summary: Option<(u64, u64)>,
325    ) -> Result<(), ExecutionError> {
326        let mut result: std::result::Result<(), sui_types::error::ExecutionError> = Ok(());
327        if !is_genesis_tx && !Mode::skip_conservation_checks() {
328            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
329            let conservation_result = {
330                temporary_store
331                    .check_sui_conserved(simple_conservation_checks, cost_summary)
332                    .and_then(|()| {
333                        if enable_expensive_checks {
334                            // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
335                            let mut layout_resolver =
336                                TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
337                            temporary_store.check_sui_conserved_expensive(
338                                cost_summary,
339                                advance_epoch_gas_summary,
340                                &mut layout_resolver,
341                            )
342                        } else {
343                            Ok(())
344                        }
345                    })
346            };
347            if let Err(conservation_err) = conservation_result {
348                // conservation violated. try to avoid panic by dumping all writes, charging for gas, re-checking
349                // conservation, and surfacing an aborted transaction with an invariant violation if all of that works
350                result = Err(conservation_err);
351                gas_charger.reset(temporary_store);
352                gas_charger.charge_gas(temporary_store, &mut result);
353                // check conservation once more
354                if let Err(recovery_err) = {
355                    temporary_store
356                        .check_sui_conserved(simple_conservation_checks, cost_summary)
357                        .and_then(|()| {
358                            if enable_expensive_checks {
359                                // ensure that this transaction did not create or destroy SUI, try to recover if the check fails
360                                let mut layout_resolver =
361                                    TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
362                                temporary_store.check_sui_conserved_expensive(
363                                    cost_summary,
364                                    advance_epoch_gas_summary,
365                                    &mut layout_resolver,
366                                )
367                            } else {
368                                Ok(())
369                            }
370                        })
371                } {
372                    // if we still fail, it's a problem with gas
373                    // charging that happens even in the "aborted" case--no other option but panic.
374                    // we will create or destroy SUI otherwise
375                    panic!(
376                        "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
377                        tx_ctx.digest(),
378                        recovery_err,
379                        gas_charger.summary()
380                    )
381                }
382            }
383        } // else, we're in the genesis transaction which mints the SUI supply, and hence does not satisfy SUI conservation, or
384          // we're in the non-production dev inspect mode which allows us to violate conservation
385        result
386    }
387
388    #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
389    fn check_meter_limit(
390        temporary_store: &mut TemporaryStore<'_>,
391        gas_charger: &mut GasCharger,
392        protocol_config: &ProtocolConfig,
393        metrics: Arc<ExecutionMetrics>,
394    ) -> Result<(), ExecutionError> {
395        let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
396
397        // Check if a limit threshold was crossed.
398        // For metered transactions, there is not soft limit.
399        // For system transactions, we allow a soft limit with alerting, and a hard limit where we terminate
400        match check_limit_by_meter!(
401            !gas_charger.is_unmetered(),
402            effects_estimated_size,
403            protocol_config.max_serialized_tx_effects_size_bytes(),
404            protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
405            metrics.limits_metrics.excessive_estimated_effects_size
406        ) {
407            LimitThresholdCrossed::None => Ok(()),
408            LimitThresholdCrossed::Soft(_, limit) => {
409                warn!(
410                    effects_estimated_size = effects_estimated_size,
411                    soft_limit = limit,
412                    "Estimated transaction effects size crossed soft limit",
413                );
414                Ok(())
415            }
416            LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
417                ExecutionErrorKind::EffectsTooLarge {
418                    current_size: effects_estimated_size as u64,
419                    max_size: lim as u64,
420                },
421                "Transaction effects are too large",
422            )),
423        }
424    }
425
426    #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
427    fn check_written_objects_limit(
428        temporary_store: &mut TemporaryStore<'_>,
429        gas_charger: &mut GasCharger,
430        protocol_config: &ProtocolConfig,
431        metrics: Arc<ExecutionMetrics>,
432    ) -> Result<(), ExecutionError> {
433        if let (Some(normal_lim), Some(system_lim)) = (
434            protocol_config.max_size_written_objects_as_option(),
435            protocol_config.max_size_written_objects_system_tx_as_option(),
436        ) {
437            let written_objects_size = temporary_store.written_objects_size();
438
439            match check_limit_by_meter!(
440                !gas_charger.is_unmetered(),
441                written_objects_size,
442                normal_lim,
443                system_lim,
444                metrics.limits_metrics.excessive_written_objects_size
445            ) {
446                LimitThresholdCrossed::None => (),
447                LimitThresholdCrossed::Soft(_, limit) => {
448                    warn!(
449                        written_objects_size = written_objects_size,
450                        soft_limit = limit,
451                        "Written objects size crossed soft limit",
452                    )
453                }
454                LimitThresholdCrossed::Hard(_, lim) => {
455                    return Err(ExecutionError::new_with_source(
456                        ExecutionErrorKind::WrittenObjectsTooLarge {
457                            current_size: written_objects_size as u64,
458                            max_size: lim as u64,
459                        },
460                        "Written objects size crossed hard limit",
461                    ));
462                }
463            };
464        }
465
466        Ok(())
467    }
468
469    #[instrument(level = "debug", skip_all)]
470    fn execution_loop<Mode: ExecutionMode>(
471        temporary_store: &mut TemporaryStore<'_>,
472        transaction_kind: TransactionKind,
473        tx_ctx: &mut TxContext,
474        move_vm: &Arc<MoveVM>,
475        gas_charger: &mut GasCharger,
476        protocol_config: &ProtocolConfig,
477        metrics: Arc<ExecutionMetrics>,
478    ) -> Result<Mode::ExecutionResults, ExecutionError> {
479        let result = match transaction_kind {
480            TransactionKind::ChangeEpoch(change_epoch) => {
481                let builder = ProgrammableTransactionBuilder::new();
482                advance_epoch(
483                    builder,
484                    change_epoch,
485                    temporary_store,
486                    tx_ctx,
487                    move_vm,
488                    gas_charger,
489                    protocol_config,
490                    metrics,
491                )?;
492                Ok(Mode::empty_results())
493            }
494            TransactionKind::Genesis(GenesisTransaction { objects }) => {
495                if tx_ctx.epoch() != 0 {
496                    panic!("BUG: Genesis Transactions can only be executed in epoch 0");
497                }
498
499                for genesis_object in objects {
500                    match genesis_object {
501                        sui_types::transaction::GenesisObject::RawObject { data, owner } => {
502                            let object = ObjectInner {
503                                data,
504                                owner,
505                                previous_transaction: tx_ctx.digest(),
506                                storage_rebate: 0,
507                            };
508                            temporary_store.create_object(object.into());
509                        }
510                    }
511                }
512                Ok(Mode::empty_results())
513            }
514            TransactionKind::ConsensusCommitPrologue(prologue) => {
515                setup_consensus_commit(
516                    prologue.commit_timestamp_ms,
517                    temporary_store,
518                    tx_ctx,
519                    move_vm,
520                    gas_charger,
521                    protocol_config,
522                    metrics,
523                )
524                .expect("ConsensusCommitPrologue cannot fail");
525                Ok(Mode::empty_results())
526            }
527            TransactionKind::ConsensusCommitPrologueV2(prologue) => {
528                setup_consensus_commit(
529                    prologue.commit_timestamp_ms,
530                    temporary_store,
531                    tx_ctx,
532                    move_vm,
533                    gas_charger,
534                    protocol_config,
535                    metrics,
536                )
537                .expect("ConsensusCommitPrologueV2 cannot fail");
538                Ok(Mode::empty_results())
539            }
540            TransactionKind::ConsensusCommitPrologueV3(prologue) => {
541                setup_consensus_commit(
542                    prologue.commit_timestamp_ms,
543                    temporary_store,
544                    tx_ctx,
545                    move_vm,
546                    gas_charger,
547                    protocol_config,
548                    metrics,
549                )
550                .expect("ConsensusCommitPrologueV3 cannot fail");
551                Ok(Mode::empty_results())
552            }
553            TransactionKind::ConsensusCommitPrologueV4(prologue) => {
554                setup_consensus_commit(
555                    prologue.commit_timestamp_ms,
556                    temporary_store,
557                    tx_ctx,
558                    move_vm,
559                    gas_charger,
560                    protocol_config,
561                    metrics,
562                )
563                .expect("ConsensusCommitPrologue cannot fail");
564                Ok(Mode::empty_results())
565            }
566            TransactionKind::ProgrammableTransaction(pt) => {
567                programmable_transactions::execution::execute::<Mode>(
568                    protocol_config,
569                    metrics,
570                    move_vm,
571                    temporary_store,
572                    tx_ctx,
573                    gas_charger,
574                    pt,
575                )
576            }
577            TransactionKind::EndOfEpochTransaction(txns) => {
578                let mut builder = ProgrammableTransactionBuilder::new();
579                let len = txns.len();
580                for (i, tx) in txns.into_iter().enumerate() {
581                    match tx {
582                        EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
583                            assert_eq!(i, len - 1);
584                            advance_epoch(
585                                builder,
586                                change_epoch,
587                                temporary_store,
588                                tx_ctx,
589                                move_vm,
590                                gas_charger,
591                                protocol_config,
592                                metrics,
593                            )?;
594                            return Ok(Mode::empty_results());
595                        }
596                        EndOfEpochTransactionKind::AuthenticatorStateCreate => {
597                            assert!(protocol_config.enable_jwk_consensus_updates());
598                            builder = setup_authenticator_state_create(builder);
599                        }
600                        EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
601                            assert!(protocol_config.enable_jwk_consensus_updates());
602
603                            // TODO: it would be nice if a failure of this function didn't cause
604                            // safe mode.
605                            builder = setup_authenticator_state_expire(builder, expire);
606                        }
607                        EndOfEpochTransactionKind::RandomnessStateCreate => {
608                            assert!(protocol_config.random_beacon());
609                            builder = setup_randomness_state_create(builder);
610                        }
611                        EndOfEpochTransactionKind::DenyListStateCreate => {
612                            assert!(protocol_config.enable_coin_deny_list());
613                            builder = setup_coin_deny_list_state_create(builder);
614                        }
615                        EndOfEpochTransactionKind::BridgeStateCreate(_) => {
616                            panic!(
617                                "EndOfEpochTransactionKind::BridgeStateCreate should not exist in v2"
618                            );
619                        }
620                        EndOfEpochTransactionKind::BridgeCommitteeInit(_) => {
621                            panic!(
622                                "EndOfEpochTransactionKind::BridgeCommitteeInit should not exist in v2"
623                            );
624                        }
625                        EndOfEpochTransactionKind::StoreExecutionTimeObservations(_) => {
626                            panic!(
627                                "EndOfEpochTransactionKind::StoreExecutionTimeEstimates should not exist in v2"
628                            );
629                        }
630                        EndOfEpochTransactionKind::AccumulatorRootCreate => {
631                            panic!(
632                                "EndOfEpochTransactionKind::AccumulatorRootCreate should not exist in v2"
633                            );
634                        }
635                        EndOfEpochTransactionKind::WriteAccumulatorStorageCost(_) => {
636                            panic!(
637                                "EndOfEpochTransactionKind::WriteAccumulatorStorageCost should not exist in v2"
638                            );
639                        }
640                        EndOfEpochTransactionKind::CoinRegistryCreate => {
641                            panic!(
642                                "EndOfEpochTransactionKind::CoinRegistryCreate should not exist in v2"
643                            );
644                        }
645                        EndOfEpochTransactionKind::DisplayRegistryCreate => {
646                            panic!(
647                                "EndOfEpochTransactionKind::DisplayRegistryCreate should not exist in v2"
648                            );
649                        }
650                        EndOfEpochTransactionKind::AddressAliasStateCreate => {
651                            panic!(
652                                "EndOfEpochTransactionKind::AddressAliasStateCreate should not exist in v2"
653                            );
654                        }
655                        EndOfEpochTransactionKind::ForwardingAddressRegistryCreate => {
656                            panic!(
657                                "EndOfEpochTransactionKind::ForwardingAddressRegistryCreate should not exist in v2"
658                            );
659                        }
660                    }
661                }
662                unreachable!(
663                    "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
664                )
665            }
666            TransactionKind::AuthenticatorStateUpdate(auth_state_update) => {
667                setup_authenticator_state_update(
668                    auth_state_update,
669                    temporary_store,
670                    tx_ctx,
671                    move_vm,
672                    gas_charger,
673                    protocol_config,
674                    metrics,
675                )?;
676                Ok(Mode::empty_results())
677            }
678            TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
679                setup_randomness_state_update(
680                    randomness_state_update,
681                    temporary_store,
682                    tx_ctx,
683                    move_vm,
684                    gas_charger,
685                    protocol_config,
686                    metrics,
687                )?;
688                Ok(Mode::empty_results())
689            }
690            TransactionKind::ProgrammableSystemTransaction(_) => {
691                panic!("ProgrammableSystemTransaction should not exist in execution layer v2");
692            }
693        }?;
694        temporary_store.check_execution_results_consistency()?;
695        Ok(result)
696    }
697
698    fn mint_epoch_rewards_in_pt(
699        builder: &mut ProgrammableTransactionBuilder,
700        params: &AdvanceEpochParams,
701    ) -> (Argument, Argument) {
702        // Create storage rewards.
703        let storage_charge_arg = builder
704            .input(CallArg::Pure(
705                bcs::to_bytes(&params.storage_charge).unwrap(),
706            ))
707            .unwrap();
708        let storage_rewards = builder.programmable_move_call(
709            SUI_FRAMEWORK_PACKAGE_ID,
710            BALANCE_MODULE_NAME.to_owned(),
711            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
712            vec![GAS::type_tag()],
713            vec![storage_charge_arg],
714        );
715
716        // Create computation rewards.
717        let computation_charge_arg = builder
718            .input(CallArg::Pure(
719                bcs::to_bytes(&params.computation_charge).unwrap(),
720            ))
721            .unwrap();
722        let computation_rewards = builder.programmable_move_call(
723            SUI_FRAMEWORK_PACKAGE_ID,
724            BALANCE_MODULE_NAME.to_owned(),
725            BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
726            vec![GAS::type_tag()],
727            vec![computation_charge_arg],
728        );
729        (storage_rewards, computation_rewards)
730    }
731
732    pub fn construct_advance_epoch_pt(
733        mut builder: ProgrammableTransactionBuilder,
734        params: &AdvanceEpochParams,
735    ) -> Result<ProgrammableTransaction, ExecutionError> {
736        // Step 1: Create storage and computation rewards.
737        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
738
739        // Step 2: Advance the epoch.
740        let mut arguments = vec![storage_rewards, computation_rewards];
741        let call_arg_arguments = vec![
742            CallArg::SUI_SYSTEM_MUT,
743            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
744            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
745            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
746            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
747            CallArg::Pure(bcs::to_bytes(&params.storage_fund_reinvest_rate).unwrap()),
748            CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()),
749            CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()),
750        ]
751        .into_iter()
752        .map(|a| builder.input(a))
753        .collect::<Result<_, _>>();
754
755        assert_invariant!(
756            call_arg_arguments.is_ok(),
757            "Unable to generate args for advance_epoch transaction!"
758        );
759
760        arguments.append(&mut call_arg_arguments.unwrap());
761
762        info!("Call arguments to advance_epoch transaction: {:?}", params);
763
764        let storage_rebates = builder.programmable_move_call(
765            SUI_SYSTEM_PACKAGE_ID,
766            SUI_SYSTEM_MODULE_NAME.to_owned(),
767            ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
768            vec![],
769            arguments,
770        );
771
772        // Step 3: Destroy the storage rebates.
773        builder.programmable_move_call(
774            SUI_FRAMEWORK_PACKAGE_ID,
775            BALANCE_MODULE_NAME.to_owned(),
776            BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
777            vec![GAS::type_tag()],
778            vec![storage_rebates],
779        );
780        Ok(builder.finish())
781    }
782
783    pub fn construct_advance_epoch_safe_mode_pt(
784        params: &AdvanceEpochParams,
785        protocol_config: &ProtocolConfig,
786    ) -> Result<ProgrammableTransaction, ExecutionError> {
787        let mut builder = ProgrammableTransactionBuilder::new();
788        // Step 1: Create storage and computation rewards.
789        let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
790
791        // Step 2: Advance the epoch.
792        let mut arguments = vec![storage_rewards, computation_rewards];
793
794        let mut args = vec![
795            CallArg::SUI_SYSTEM_MUT,
796            CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()),
797            CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()),
798            CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()),
799            CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()),
800        ];
801
802        if protocol_config.advance_epoch_start_time_in_safe_mode() {
803            args.push(CallArg::Pure(
804                bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap(),
805            ));
806        }
807
808        let call_arg_arguments = args
809            .into_iter()
810            .map(|a| builder.input(a))
811            .collect::<Result<_, _>>();
812
813        assert_invariant!(
814            call_arg_arguments.is_ok(),
815            "Unable to generate args for advance_epoch transaction!"
816        );
817
818        arguments.append(&mut call_arg_arguments.unwrap());
819
820        info!("Call arguments to advance_epoch transaction: {:?}", params);
821
822        builder.programmable_move_call(
823            SUI_SYSTEM_PACKAGE_ID,
824            SUI_SYSTEM_MODULE_NAME.to_owned(),
825            ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
826            vec![],
827            arguments,
828        );
829
830        Ok(builder.finish())
831    }
832
833    fn advance_epoch(
834        builder: ProgrammableTransactionBuilder,
835        change_epoch: ChangeEpoch,
836        temporary_store: &mut TemporaryStore<'_>,
837        tx_ctx: &mut TxContext,
838        move_vm: &Arc<MoveVM>,
839        gas_charger: &mut GasCharger,
840        protocol_config: &ProtocolConfig,
841        metrics: Arc<ExecutionMetrics>,
842    ) -> Result<(), ExecutionError> {
843        let params = AdvanceEpochParams {
844            epoch: change_epoch.epoch,
845            next_protocol_version: change_epoch.protocol_version,
846            storage_charge: change_epoch.storage_charge,
847            computation_charge: change_epoch.computation_charge,
848            storage_rebate: change_epoch.storage_rebate,
849            non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
850            storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
851            reward_slashing_rate: protocol_config.reward_slashing_rate(),
852            epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
853        };
854        let advance_epoch_pt = construct_advance_epoch_pt(builder, &params)?;
855        let result = programmable_transactions::execution::execute::<execution_mode::System>(
856            protocol_config,
857            metrics.clone(),
858            move_vm,
859            temporary_store,
860            tx_ctx,
861            gas_charger,
862            advance_epoch_pt,
863        );
864
865        #[cfg(msim)]
866        let result = maybe_modify_result_legacy(result, change_epoch.epoch);
867
868        if result.is_err() {
869            tracing::error!(
870                "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
871                result.as_ref().err(),
872                temporary_store.objects(),
873                change_epoch,
874            );
875            temporary_store.drop_writes();
876            // Must reset the storage rebate since we are re-executing.
877            gas_charger.reset_storage_cost_and_rebate();
878
879            if protocol_config.advance_epoch_start_time_in_safe_mode() {
880                temporary_store.advance_epoch_safe_mode(&params, protocol_config);
881            } else {
882                let advance_epoch_safe_mode_pt =
883                    construct_advance_epoch_safe_mode_pt(&params, protocol_config)?;
884                programmable_transactions::execution::execute::<execution_mode::System>(
885                    protocol_config,
886                    metrics.clone(),
887                    move_vm,
888                    temporary_store,
889                    tx_ctx,
890                    gas_charger,
891                    advance_epoch_safe_mode_pt,
892                )
893                .expect("Advance epoch with safe mode must succeed");
894            }
895        }
896
897        let binary_config = protocol_config.binary_config(None);
898        for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
899            let deserialized_modules: Vec<_> = modules
900                .iter()
901                .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
902                .collect();
903
904            if version == OBJECT_START_VERSION {
905                let package_id = deserialized_modules.first().unwrap().address();
906                info!("adding new system package {package_id}");
907
908                let publish_pt = {
909                    let mut b = ProgrammableTransactionBuilder::new();
910                    b.command(Command::Publish(modules, dependencies));
911                    b.finish()
912                };
913
914                programmable_transactions::execution::execute::<execution_mode::System>(
915                    protocol_config,
916                    metrics.clone(),
917                    move_vm,
918                    temporary_store,
919                    tx_ctx,
920                    gas_charger,
921                    publish_pt,
922                )
923                .expect("System Package Publish must succeed");
924            } else {
925                let mut new_package = Object::new_system_package(
926                    &deserialized_modules,
927                    version,
928                    dependencies,
929                    tx_ctx.digest(),
930                );
931
932                info!(
933                    "upgraded system package {:?}",
934                    new_package.compute_object_reference()
935                );
936
937                // Decrement the version before writing the package so that the store can record the
938                // version growing by one in the effects.
939                new_package
940                    .data
941                    .try_as_package_mut()
942                    .unwrap()
943                    .decrement_version();
944
945                // upgrade of a previously existing framework module
946                temporary_store.upgrade_system_package(new_package);
947            }
948        }
949
950        Ok(())
951    }
952
953    /// Perform metadata updates in preparation for the transactions in the upcoming checkpoint:
954    ///
955    /// - Set the timestamp for the `Clock` shared object from the timestamp in the header from
956    ///   consensus.
957    fn setup_consensus_commit(
958        consensus_commit_timestamp_ms: CheckpointTimestamp,
959        temporary_store: &mut TemporaryStore<'_>,
960        tx_ctx: &mut TxContext,
961        move_vm: &Arc<MoveVM>,
962        gas_charger: &mut GasCharger,
963        protocol_config: &ProtocolConfig,
964        metrics: Arc<ExecutionMetrics>,
965    ) -> Result<(), ExecutionError> {
966        let pt = {
967            let mut builder = ProgrammableTransactionBuilder::new();
968            let res = builder.move_call(
969                SUI_FRAMEWORK_ADDRESS.into(),
970                CLOCK_MODULE_NAME.to_owned(),
971                CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
972                vec![],
973                vec![
974                    CallArg::CLOCK_MUT,
975                    CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
976                ],
977            );
978            assert_invariant!(
979                res.is_ok(),
980                "Unable to generate consensus_commit_prologue transaction!"
981            );
982            builder.finish()
983        };
984        programmable_transactions::execution::execute::<execution_mode::System>(
985            protocol_config,
986            metrics,
987            move_vm,
988            temporary_store,
989            tx_ctx,
990            gas_charger,
991            pt,
992        )
993    }
994
995    fn setup_authenticator_state_create(
996        mut builder: ProgrammableTransactionBuilder,
997    ) -> ProgrammableTransactionBuilder {
998        builder
999            .move_call(
1000                SUI_FRAMEWORK_ADDRESS.into(),
1001                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1002                AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1003                vec![],
1004                vec![],
1005            )
1006            .expect("Unable to generate authenticator_state_create transaction!");
1007        builder
1008    }
1009
1010    fn setup_randomness_state_create(
1011        mut builder: ProgrammableTransactionBuilder,
1012    ) -> ProgrammableTransactionBuilder {
1013        builder
1014            .move_call(
1015                SUI_FRAMEWORK_ADDRESS.into(),
1016                RANDOMNESS_MODULE_NAME.to_owned(),
1017                RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
1018                vec![],
1019                vec![],
1020            )
1021            .expect("Unable to generate randomness_state_create transaction!");
1022        builder
1023    }
1024
1025    fn setup_authenticator_state_update(
1026        update: AuthenticatorStateUpdate,
1027        temporary_store: &mut TemporaryStore<'_>,
1028        tx_ctx: &mut TxContext,
1029        move_vm: &Arc<MoveVM>,
1030        gas_charger: &mut GasCharger,
1031        protocol_config: &ProtocolConfig,
1032        metrics: Arc<ExecutionMetrics>,
1033    ) -> Result<(), ExecutionError> {
1034        let pt = {
1035            let mut builder = ProgrammableTransactionBuilder::new();
1036            let res = builder.move_call(
1037                SUI_FRAMEWORK_ADDRESS.into(),
1038                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1039                AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1040                vec![],
1041                vec![
1042                    CallArg::Object(ObjectArg::SharedObject {
1043                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1044                        initial_shared_version: update.authenticator_obj_initial_shared_version,
1045                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1046                    }),
1047                    CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1048                ],
1049            );
1050            assert_invariant!(
1051                res.is_ok(),
1052                "Unable to generate authenticator_state_update transaction!"
1053            );
1054            builder.finish()
1055        };
1056        programmable_transactions::execution::execute::<execution_mode::System>(
1057            protocol_config,
1058            metrics,
1059            move_vm,
1060            temporary_store,
1061            tx_ctx,
1062            gas_charger,
1063            pt,
1064        )
1065    }
1066
1067    fn setup_authenticator_state_expire(
1068        mut builder: ProgrammableTransactionBuilder,
1069        expire: AuthenticatorStateExpire,
1070    ) -> ProgrammableTransactionBuilder {
1071        builder
1072            .move_call(
1073                SUI_FRAMEWORK_ADDRESS.into(),
1074                AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1075                AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1076                vec![],
1077                vec![
1078                    CallArg::Object(ObjectArg::SharedObject {
1079                        id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
1080                        initial_shared_version: expire.authenticator_obj_initial_shared_version,
1081                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1082                    }),
1083                    CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1084                ],
1085            )
1086            .expect("Unable to generate authenticator_state_expire transaction!");
1087        builder
1088    }
1089
1090    fn setup_randomness_state_update(
1091        update: RandomnessStateUpdate,
1092        temporary_store: &mut TemporaryStore<'_>,
1093        tx_ctx: &mut TxContext,
1094        move_vm: &Arc<MoveVM>,
1095        gas_charger: &mut GasCharger,
1096        protocol_config: &ProtocolConfig,
1097        metrics: Arc<ExecutionMetrics>,
1098    ) -> Result<(), ExecutionError> {
1099        let pt = {
1100            let mut builder = ProgrammableTransactionBuilder::new();
1101            let res = builder.move_call(
1102                SUI_FRAMEWORK_ADDRESS.into(),
1103                RANDOMNESS_MODULE_NAME.to_owned(),
1104                RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1105                vec![],
1106                vec![
1107                    CallArg::Object(ObjectArg::SharedObject {
1108                        id: SUI_RANDOMNESS_STATE_OBJECT_ID,
1109                        initial_shared_version: update.randomness_obj_initial_shared_version,
1110                        mutability: sui_types::transaction::SharedObjectMutability::Mutable,
1111                    }),
1112                    CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1113                    CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1114                ],
1115            );
1116            assert_invariant!(
1117                res.is_ok(),
1118                "Unable to generate randomness_state_update transaction!"
1119            );
1120            builder.finish()
1121        };
1122        programmable_transactions::execution::execute::<execution_mode::System>(
1123            protocol_config,
1124            metrics,
1125            move_vm,
1126            temporary_store,
1127            tx_ctx,
1128            gas_charger,
1129            pt,
1130        )
1131    }
1132
1133    fn setup_coin_deny_list_state_create(
1134        mut builder: ProgrammableTransactionBuilder,
1135    ) -> ProgrammableTransactionBuilder {
1136        builder
1137            .move_call(
1138                SUI_FRAMEWORK_ADDRESS.into(),
1139                DENY_LIST_MODULE.to_owned(),
1140                DENY_LIST_CREATE_FUNC.to_owned(),
1141                vec![],
1142                vec![],
1143            )
1144            .expect("Unable to generate coin_deny_list_create transaction!");
1145        builder
1146    }
1147}