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