1pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7pub(crate) mod checked {
8
9 use crate::adapter::new_move_runtime;
10 use crate::execution_mode::{self, ExecutionMode};
11 use crate::gas_charger::{PaymentKind, PaymentMethod};
12 use move_binary_format::CompiledModule;
13 use move_trace_format::format::MoveTraceBuilder;
14 use move_vm_runtime::runtime::MoveRuntime;
15 use mysten_common::{
16 assert_reachable, debug_fatal, debug_fatal_with_metric, in_test_configuration,
17 };
18 use std::collections::{BTreeMap, BTreeSet};
19 use std::{cell::RefCell, rc::Rc, sync::Arc};
20 use sui_types::accumulator_root::{
21 ACCUMULATOR_ROOT_CREATE_FUNC, ACCUMULATOR_ROOT_MODULE, UnsettledObjectFundsRead,
22 };
23 use sui_types::balance::{
24 BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
25 BALANCE_MODULE_NAME,
26 };
27 use sui_types::coin_reservation::ParsedDigest;
28 use sui_types::execution_params::ExecutionOrEarlyError;
29 use sui_types::gas_coin::GAS;
30 use sui_types::gas_model::gas_predicates::bump_only_enabled;
31 use sui_types::messages_checkpoint::CheckpointTimestamp;
32 use sui_types::metrics::ExecutionMetrics;
33 use sui_types::object::OBJECT_START_VERSION;
34 use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
35 use sui_types::randomness_state::{
36 RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_CREATE_FUNCTION_NAME,
37 RANDOMNESS_STATE_UPDATE_FUNCTION_NAME,
38 };
39 use sui_types::{BRIDGE_ADDRESS, SUI_BRIDGE_OBJECT_ID, SUI_RANDOMNESS_STATE_OBJECT_ID};
40 use tracing::{info, instrument, trace, warn};
41
42 use crate::static_programmable_transactions as SPT;
43 use crate::sui_types::gas::SuiGasStatusAPI;
44 use crate::{gas_charger::GasCharger, temporary_store::TemporaryStore};
45 use move_core_types::ident_str;
46 use move_core_types::language_storage::TypeTag;
47 use sui_move_natives::all_natives;
48 use sui_protocol_config::{
49 Chain, LimitThresholdCrossed, PerObjectCongestionControlMode, ProtocolConfig,
50 check_limit_by_meter,
51 };
52 use sui_types::authenticator_state::{
53 AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME, AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME,
54 AUTHENTICATOR_STATE_MODULE_NAME, AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
55 };
56 use sui_types::base_types::{ObjectID, SequenceNumber, SystemObjectVersions};
57 use sui_types::bridge::BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER;
58 use sui_types::bridge::{
59 BRIDGE_CREATE_FUNCTION_NAME, BRIDGE_INIT_COMMITTEE_FUNCTION_NAME, BRIDGE_MODULE_NAME,
60 BridgeChainId,
61 };
62 use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME};
63 use sui_types::committee::EpochId;
64 use sui_types::deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE};
65 use sui_types::digests::{
66 ChainIdentifier, get_mainnet_chain_identifier, get_testnet_chain_identifier,
67 };
68 use sui_types::effects::TransactionEffects;
69 use sui_types::error::{ExecutionError, ExecutionErrorTrait};
70 use sui_types::execution::{ExecutionTiming, ResultWithTimings, SharedInput};
71 use sui_types::execution_status::{ExecutionErrorKind, ExecutionStatus};
72 use sui_types::gas::GasCostSummary;
73 use sui_types::gas::SuiGasStatus;
74 use sui_types::id::UID;
75 use sui_types::inner_temporary_store::InnerTemporaryStore;
76 use sui_types::storage::BackingStore;
77 #[cfg(msim)]
78 use sui_types::sui_system_state::advance_epoch_result_injection::maybe_modify_result_for;
79 use sui_types::sui_system_state::{ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME, AdvanceEpochParams};
80 use sui_types::transaction::{
81 Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, CallArg, ChangeEpoch,
82 Command, EndOfEpochTransactionKind, GasData, GenesisTransaction, ObjectArg,
83 ProgrammableTransaction, Reservation, StoredExecutionTimeObservations, TransactionKind,
84 WithdrawFrom, WriteAccumulatorStorageCost, is_gasless_transaction,
85 };
86 use sui_types::transaction::{CheckedInputObjects, RandomnessStateUpdate};
87 use sui_types::{
88 SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_FRAMEWORK_PACKAGE_ID,
89 SUI_SYSTEM_PACKAGE_ID,
90 base_types::{SuiAddress, TransactionDigest, TxContext},
91 object::{Object, ObjectInner},
92 sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME},
93 };
94
95 fn head_error_is_insufficient_funds_for_withdraw(
97 execution_params: &ExecutionOrEarlyError,
98 ) -> bool {
99 execution_params.early_errors().is_some_and(|errors| {
100 matches!(
101 errors.head,
102 ExecutionErrorKind::InsufficientFundsForWithdraw
103 )
104 })
105 }
106
107 fn any_error_is_insufficient_funds_for_withdraw(
109 execution_params: &ExecutionOrEarlyError,
110 ) -> bool {
111 execution_params.early_errors().is_some_and(|errors| {
112 errors
113 .iter()
114 .any(|e| matches!(e, ExecutionErrorKind::InsufficientFundsForWithdraw))
115 })
116 }
117
118 fn should_short_circuit_insufficient_funds(execution_params: &ExecutionOrEarlyError) -> bool {
122 any_error_is_insufficient_funds_for_withdraw(execution_params)
123 }
124
125 fn payment_kind(
126 gas_data: &GasData,
127 transaction_kind: &TransactionKind,
128 ) -> Result<PaymentKind, ExecutionError> {
129 Ok(
130 if gas_data.is_unmetered() || transaction_kind.is_system_tx() {
131 PaymentKind::unmetered()
132 } else if is_gasless_transaction(gas_data, transaction_kind) {
133 PaymentKind::gasless()
134 } else if gas_data.payment.is_empty() {
135 PaymentKind::smash(vec![PaymentMethod::AddressBalance(
136 gas_data.owner,
137 gas_data.budget,
138 )])
139 .ok_or_else(|| {
140 ExecutionError::invariant_violation(
141 "unable to create a payment kind with a single address balance",
142 )
143 })?
144 } else {
145 let payment_methods = gas_data
146 .payment
147 .iter()
148 .map(|entry| {
149 if let Ok(parsed) = ParsedDigest::try_from(entry.2) {
150 PaymentMethod::AddressBalance(
151 gas_data.owner,
152 parsed.reservation_amount(),
153 )
154 } else {
155 PaymentMethod::Coin(*entry)
156 }
157 })
158 .collect();
159 PaymentKind::smash(payment_methods).ok_or_else(|| {
160 ExecutionError::invariant_violation(
161 "unable to create a payment kind from the gas payment: \
162 duplicate gas coin or reservation overflow",
163 )
164 })?
165 },
166 )
167 }
168
169 fn legacy_payment_kind(
171 gas_data: &GasData,
172 transaction_kind: &TransactionKind,
173 protocol_config: &ProtocolConfig,
174 ) -> PaymentKind {
175 if gas_data.is_unmetered() || transaction_kind.is_system_tx() {
176 PaymentKind::unmetered()
177 } else if protocol_config.enable_gasless()
178 && is_gasless_transaction(gas_data, transaction_kind)
179 {
180 PaymentKind::gasless()
181 } else if gas_data.payment.is_empty() {
182 PaymentKind::smash(vec![PaymentMethod::AddressBalance(
183 gas_data.owner,
184 gas_data.budget,
185 )])
186 .expect("unable to create a payment kind with a single address balance")
187 } else {
188 let payment_methods = gas_data
189 .payment
190 .iter()
191 .map(|entry| {
192 if let Ok(parsed) = ParsedDigest::try_from(entry.2) {
193 PaymentMethod::AddressBalance(gas_data.owner, parsed.reservation_amount())
194 } else {
195 PaymentMethod::Coin(*entry)
196 }
197 })
198 .collect();
199 PaymentKind::smash(payment_methods).expect(
200 "unable to create a payment kind from payment methods. \
201 Should not be possible wit ha non-empty vector",
202 )
203 }
204 }
205
206 pub struct ExecutionOutput<Mode: ExecutionMode> {
208 pub inner_store: InnerTemporaryStore,
209 pub gas_status: SuiGasStatus,
210 pub effects: TransactionEffects,
211 pub timings: Vec<ExecutionTiming>,
212 pub execution_result: Result<Mode::ExecutionResults, Mode::Error>,
213 }
214
215 struct ExecutionOutcome<Mode: ExecutionMode> {
217 cost_summary: GasCostSummary,
218 execution_result: Result<Mode::ExecutionResults, Mode::Error>,
219 timings: Vec<ExecutionTiming>,
220 }
221 #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
222 pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
223 store: &dyn BackingStore,
224 input_objects: CheckedInputObjects,
225 system_object_versions: SystemObjectVersions,
226 unsettled_object_funds: &dyn UnsettledObjectFundsRead,
227 mut gas_data: GasData,
228 gas_status: SuiGasStatus,
229 transaction_kind: TransactionKind,
230 rewritten_inputs: Option<Vec<bool>>,
231 transaction_signer: SuiAddress,
232 transaction_digest: TransactionDigest,
233 move_vm: &Arc<MoveRuntime>,
234 epoch_id: &EpochId,
235 epoch_timestamp_ms: u64,
236 protocol_config: &ProtocolConfig,
237 metrics: Arc<ExecutionMetrics>,
238 enable_expensive_checks: bool,
239 execution_params: ExecutionOrEarlyError,
240 trace_builder_opt: &mut Option<MoveTraceBuilder>,
241 ) -> ExecutionOutput<Mode> {
242 let input_objects = input_objects.into_inner();
243 let shared_object_refs = input_objects.filter_shared_objects();
244 let receiving_objects = transaction_kind.receiving_objects();
245 let mut transaction_dependencies = if protocol_config.disable_effects_tx_dependencies() {
246 BTreeSet::new()
247 } else {
248 input_objects.transaction_dependencies()
249 };
250
251 if !bump_only_enabled(protocol_config.gas_model_version()) {
254 legacy::iffw_filter_address_balance_gas_payments(
255 &mut gas_data,
256 &execution_params,
257 protocol_config,
258 );
259 }
260
261 let mut temporary_store = TemporaryStore::new(
262 store,
263 input_objects,
264 receiving_objects,
265 transaction_digest,
266 protocol_config,
267 *epoch_id,
268 system_object_versions,
269 (&transaction_kind, &gas_data, transaction_signer),
270 unsettled_object_funds,
271 );
272
273 if bump_only_enabled(protocol_config.gas_model_version()) {
274 let Finalized {
275 gas,
276 status,
277 timings,
278 execution_result,
279 }: Finalized<Mode> = match execute_transaction_to_outcome::<Mode>(
280 store,
281 &mut temporary_store,
282 gas_data,
283 gas_status,
284 transaction_kind,
285 rewritten_inputs,
286 transaction_signer,
287 transaction_digest,
288 move_vm,
289 epoch_id,
290 epoch_timestamp_ms,
291 protocol_config,
292 metrics.clone(),
293 enable_expensive_checks,
294 execution_params,
295 trace_builder_opt,
296 ) {
297 Outcome::Proceed {
298 gas_charger,
299 gas_cost_summary,
300 execution_result,
301 timings,
302 } => {
303 let status = if let Err(error) = &execution_result {
304 ExecutionStatus::new_failure(error.to_execution_failure())
305 } else {
306 ExecutionStatus::Success
307 };
308 let coin = gas_charger.gas_coin();
309 Finalized {
310 gas: GasOutcome {
311 cost_summary: gas_cost_summary,
312 coin,
313 status: gas_charger.into_gas_status(),
314 },
315 status,
316 timings,
317 execution_result,
318 }
319 }
320 Outcome::BumpOnly {
321 gas_status,
322 error,
323 reason,
324 } => {
325 report_bump_only::<Mode>(reason, &transaction_digest, &error);
326 temporary_store = temporary_store.into_bump_only();
328 Finalized {
329 gas: GasOutcome {
330 cost_summary: GasCostSummary::default(),
331 coin: None,
332 status: gas_status,
333 },
334 status: ExecutionStatus::new_failure(error.to_execution_failure()),
335 timings: vec![],
336 execution_result: Err(error),
337 }
338 }
339 };
340
341 let GasOutcome {
343 cost_summary,
344 coin,
345 status: gas_status,
346 } = gas;
347 #[skip_checked_arithmetic]
348 trace!(
349 tx_digest = ?transaction_digest,
350 computation_gas_cost = cost_summary.computation_cost,
351 storage_gas_cost = cost_summary.storage_cost,
352 storage_gas_rebate = cost_summary.storage_rebate,
353 "Finished execution of transaction with status {:?}",
354 status
355 );
356 transaction_dependencies.remove(&TransactionDigest::genesis_marker());
357 let (inner, effects) = temporary_store.into_effects(
358 shared_object_refs,
359 &transaction_digest,
360 transaction_dependencies,
361 cost_summary,
362 status,
363 coin,
364 *epoch_id,
365 );
366 if !Mode::TRACK_EXECUTION {
369 update_vm_telemetry_metrics(&metrics, move_vm);
370 }
371 ExecutionOutput {
372 inner_store: inner,
373 gas_status,
374 effects,
375 timings,
376 execution_result,
377 }
378 } else {
379 legacy::execute_transaction_inner::<Mode>(
381 store,
382 temporary_store,
383 gas_data,
384 gas_status,
385 transaction_kind,
386 rewritten_inputs,
387 transaction_signer,
388 transaction_digest,
389 move_vm,
390 epoch_id,
391 epoch_timestamp_ms,
392 protocol_config,
393 metrics,
394 enable_expensive_checks,
395 execution_params,
396 trace_builder_opt,
397 shared_object_refs,
398 transaction_dependencies,
399 )
400 }
401 }
402
403 #[allow(clippy::too_many_arguments)]
408 fn check_consistency<Mode: ExecutionMode>(
409 temporary_store: &mut TemporaryStore<'_>,
410 gas_charger: &GasCharger,
411 gas_cost_summary: &GasCostSummary,
412 move_vm: &Arc<MoveRuntime>,
413 enable_expensive_checks: bool,
414 transaction_signer: SuiAddress,
415 sponsor: Option<SuiAddress>,
416 is_epoch_change: bool,
417 transaction_digest: TransactionDigest,
418 execution_succeeded: bool,
419 ) -> Result<(), (Mode::Error, BumpOnlyReason)> {
420 run_conservation_checks::<Mode>(
422 temporary_store,
423 gas_charger,
424 transaction_digest,
425 move_vm,
426 enable_expensive_checks,
427 gas_cost_summary,
428 )
429 .map_err(|error| (error, BumpOnlyReason::Conservation))?;
430
431 if enable_expensive_checks
434 && !Mode::allow_arbitrary_function_calls()
435 && let Err(err) = temporary_store.check_ownership_invariants(
436 &transaction_signer,
437 &sponsor,
438 gas_charger,
439 is_epoch_change,
440 )
441 {
442 #[skip_checked_arithmetic]
443 tracing::error!(
444 tx_digest = ?transaction_digest,
445 error = %err,
446 "ownership invariants violated; falling back to the no-op exit (State 2): \
447 dropping all writes and charging nothing",
448 );
449 return Err((
450 ExecutionError::from_kind(ExecutionErrorKind::InvariantViolation).into(),
451 BumpOnlyReason::Ownership,
452 ));
453 }
454
455 if execution_succeeded {
458 temporary_store
459 .check_published_packages()
460 .map_err(|error| (error.into(), BumpOnlyReason::PublishedPackages))?;
461 }
462
463 Ok(())
464 }
465
466 fn update_vm_telemetry_metrics(metrics: &ExecutionMetrics, move_vm: &MoveRuntime) {
467 metrics.vm_telemetry_metrics.try_update(|vm_metrics| {
468 let t = move_vm.get_telemetry_report();
469 vm_metrics
470 .move_vm_package_cache_count
471 .set(t.package_cache_count as i64);
472 vm_metrics
473 .move_vm_total_arena_size_bytes
474 .set(t.total_arena_size as i64);
475 vm_metrics.move_vm_module_count.set(t.module_count as i64);
476 vm_metrics
477 .move_vm_function_count
478 .set(t.function_count as i64);
479 vm_metrics.move_vm_type_count.set(t.type_count as i64);
480 vm_metrics.move_vm_interner_size.set(t.interner_size as i64);
481 vm_metrics
482 .move_vm_vtable_cache_count
483 .set(t.vtable_cache_count as i64);
484 vm_metrics
485 .move_vm_vtable_cache_hits
486 .set(t.vtable_cache_hits as i64);
487 vm_metrics
488 .move_vm_vtable_cache_misses
489 .set(t.vtable_cache_misses as i64);
490 vm_metrics
491 .move_vm_load_time_ms
492 .set(t.total_load_time as i64);
493 vm_metrics.move_vm_load_count.set(t.load_count as i64);
494 vm_metrics
495 .move_vm_validation_time_ms
496 .set(t.total_validation_time as i64);
497 vm_metrics
498 .move_vm_validation_count
499 .set(t.validation_count as i64);
500 vm_metrics.move_vm_jit_time_ms.set(t.total_jit_time as i64);
501 vm_metrics.move_vm_jit_count.set(t.jit_count as i64);
502 vm_metrics
503 .move_vm_execution_time_ms
504 .set(t.total_execution_time as i64);
505 vm_metrics
506 .move_vm_execution_count
507 .set(t.execution_count as i64);
508 vm_metrics
509 .move_vm_interpreter_time_ms
510 .set(t.total_interpreter_time as i64);
511 vm_metrics
512 .move_vm_interpreter_count
513 .set(t.interpreter_count as i64);
514 vm_metrics
515 .move_vm_max_callstack_size
516 .set(t.max_callstack_size as i64);
517 vm_metrics
518 .move_vm_max_valuestack_size
519 .set(t.max_valuestack_size as i64);
520 vm_metrics.move_vm_total_time_ms.set(t.total_time as i64);
521 vm_metrics.move_vm_total_count.set(t.total_count as i64);
522 });
523 }
524
525 pub fn execute_genesis_state_update(
526 store: &dyn BackingStore,
527 protocol_config: &ProtocolConfig,
528 metrics: Arc<ExecutionMetrics>,
529 move_vm: &Arc<MoveRuntime>,
530 tx_context: Rc<RefCell<TxContext>>,
531 pt: ProgrammableTransaction,
532 ) -> Result<InnerTemporaryStore, ExecutionError> {
533 let mut temporary_store = TemporaryStore::new_for_genesis_state_update(
534 store,
535 tx_context.borrow().digest(),
536 protocol_config,
537 );
538 let mut gas_charger =
539 GasCharger::new_unmetered(tx_context.borrow().digest(), protocol_config);
540 SPT::execute::<execution_mode::Genesis>(
541 protocol_config,
542 metrics,
543 move_vm,
544 &mut temporary_store,
545 store,
546 tx_context,
547 &mut gas_charger,
548 None,
549 pt,
550 &mut None,
551 )
552 .map_err(|(e, _)| e)?;
553 temporary_store.update_object_version_and_prev_tx();
554 Ok(temporary_store.into_inner(BTreeMap::new()))
555 }
556
557 #[allow(clippy::large_enum_variant)]
558 enum Outcome<Mode: ExecutionMode> {
559 Proceed {
560 gas_charger: GasCharger,
561 gas_cost_summary: GasCostSummary,
562 execution_result: Result<Mode::ExecutionResults, Mode::Error>,
563 timings: Vec<ExecutionTiming>,
564 },
565 BumpOnly {
566 gas_status: SuiGasStatus,
567 error: Mode::Error,
568 reason: BumpOnlyReason,
569 },
570 }
571
572 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
576 enum BumpOnlyReason {
577 InsufficientFundsForWithdraw,
578 GasSmash,
579 WriteReset,
580 Conservation,
581 Ownership,
582 PublishedPackages,
583 }
584
585 impl BumpOnlyReason {
586 fn label(self) -> &'static str {
588 match self {
589 Self::InsufficientFundsForWithdraw => "insufficient_funds_for_withdraw",
590 Self::GasSmash => "gas_smash",
591 Self::WriteReset => "write_reset",
592 Self::Conservation => "conservation",
593 Self::Ownership => "ownership",
594 Self::PublishedPackages => "published_packages",
595 }
596 }
597
598 fn is_expected(self) -> bool {
599 matches!(self, Self::InsufficientFundsForWithdraw)
600 }
601 }
602
603 struct GasOutcome {
604 cost_summary: GasCostSummary,
605 coin: Option<ObjectID>,
606 status: SuiGasStatus,
607 }
608
609 struct Finalized<Mode: ExecutionMode> {
610 gas: GasOutcome,
611 status: ExecutionStatus,
612 timings: Vec<ExecutionTiming>,
613 execution_result: Result<Mode::ExecutionResults, Mode::Error>,
614 }
615
616 fn report_bump_only<Mode: ExecutionMode>(
624 reason: BumpOnlyReason,
625 transaction_digest: &TransactionDigest,
626 error: &Mode::Error,
627 ) {
628 if reason.is_expected() || Mode::TRACK_EXECUTION {
629 return;
630 }
631 debug_fatal_with_metric!(
632 |metrics: &mysten_metrics::Metrics| {
633 metrics
634 .execution_bump_only_exits
635 .with_label_values(&[reason.label()])
636 .inc();
637 },
638 "BumpOnly exit: all writes dropped, no gas charged. \
639 reason={}, tx_digest={:?}, error={:?}",
640 reason.label(),
641 transaction_digest,
642 error
643 );
644 }
645
646 fn execute_transaction_to_outcome<Mode: ExecutionMode>(
647 store: &dyn BackingStore,
648 temporary_store: &mut TemporaryStore<'_>,
649 gas_data: GasData,
650 gas_status: SuiGasStatus,
651 transaction_kind: TransactionKind,
652 rewritten_inputs: Option<Vec<bool>>,
653 transaction_signer: SuiAddress,
654 transaction_digest: TransactionDigest,
655 move_vm: &Arc<MoveRuntime>,
656 epoch_id: &EpochId,
657 epoch_timestamp_ms: u64,
658 protocol_config: &ProtocolConfig,
659 metrics: Arc<ExecutionMetrics>,
660 enable_expensive_checks: bool,
661 execution_params: ExecutionOrEarlyError,
662 trace_builder_opt: &mut Option<MoveTraceBuilder>,
663 ) -> Outcome<Mode> {
664 if should_short_circuit_insufficient_funds(&execution_params) {
666 assert_reachable!("IFFW short-circuit fired");
667 let iffw: Mode::Error =
668 ExecutionError::from_kind(ExecutionErrorKind::InsufficientFundsForWithdraw).into();
669 return Outcome::BumpOnly {
670 gas_status,
671 error: iffw,
672 reason: BumpOnlyReason::InsufficientFundsForWithdraw,
673 };
674 }
675
676 let sponsor = {
677 let gas_owner = gas_data.owner;
678 if gas_owner == transaction_signer {
679 None
680 } else {
681 Some(gas_owner)
682 }
683 };
684 let gas_price = gas_status.gas_price();
685 let rgp = gas_status.reference_gas_price();
686 let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
687
688 let tx_ctx = TxContext::new_from_components(
689 &transaction_signer,
690 &transaction_digest,
691 epoch_id,
692 epoch_timestamp_ms,
693 rgp,
694 gas_price,
695 gas_data.budget,
696 sponsor,
697 protocol_config,
698 );
699 let tx_ctx = Rc::new(RefCell::new(tx_ctx));
700
701 let payment_kind = match payment_kind(&gas_data, &transaction_kind) {
702 Ok(payment_kind) => payment_kind,
703 Err(error) => {
704 return Outcome::BumpOnly {
705 gas_status,
706 error: error.into(),
707 reason: BumpOnlyReason::GasSmash,
708 };
709 }
710 };
711 let mut gas_charger = GasCharger::new(
712 transaction_digest,
713 payment_kind,
714 gas_status,
715 temporary_store,
716 protocol_config,
717 );
718 let ExecutionOutcome {
719 cost_summary: gas_cost_summary,
720 execution_result,
721 timings,
722 } = match execute_transaction::<Mode>(
723 store,
724 temporary_store,
725 transaction_kind,
726 rewritten_inputs,
727 &mut gas_charger,
728 tx_ctx,
729 move_vm,
730 protocol_config,
731 metrics,
732 execution_params,
733 trace_builder_opt,
734 ) {
735 Err((error, reason)) => {
736 return Outcome::BumpOnly {
737 gas_status: gas_charger.into_gas_status(),
738 error,
739 reason,
740 };
741 }
742 Ok(outcome) => outcome,
743 };
744
745 match check_consistency::<Mode>(
748 temporary_store,
749 &gas_charger,
750 &gas_cost_summary,
751 move_vm,
752 enable_expensive_checks,
753 transaction_signer,
754 sponsor,
755 is_epoch_change,
756 transaction_digest,
757 execution_result.is_ok(),
758 ) {
759 Ok(()) => Outcome::Proceed {
760 gas_charger,
761 gas_cost_summary,
762 execution_result,
763 timings,
764 },
765 Err((error, reason)) => Outcome::BumpOnly {
766 gas_status: gas_charger.into_gas_status(),
767 error,
768 reason,
769 },
770 }
771 }
772
773 #[instrument(name = "tx_execute", level = "debug", skip_all)]
774 fn execute_transaction<Mode: ExecutionMode>(
775 store: &dyn BackingStore,
776 temporary_store: &mut TemporaryStore<'_>,
777 transaction_kind: TransactionKind,
778 rewritten_inputs: Option<Vec<bool>>,
779 gas_charger: &mut GasCharger,
780 tx_ctx: Rc<RefCell<TxContext>>,
781 move_vm: &Arc<MoveRuntime>,
782 protocol_config: &ProtocolConfig,
783 metrics: Arc<ExecutionMetrics>,
784 execution_params: ExecutionOrEarlyError,
785 trace_builder_opt: &mut Option<MoveTraceBuilder>,
786 ) -> Result<ExecutionOutcome<Mode>, (Mode::Error, BumpOnlyReason)> {
787 debug_assert!(
788 gas_charger.no_charges(),
789 "No gas charges must be applied yet"
790 );
791
792 let mut timings: Vec<ExecutionTiming> = vec![];
793
794 let result = gas_charger
795 .charge_input_objects(temporary_store)
796 .map_err(Into::into)
797 .and_then(|()| match execution_params.into_early_errors() {
799 Some(early_execution_errors) => {
800 Err(ExecutionError::new(early_execution_errors.head, None).into())
801 }
802 None => execute_ptb::<Mode>(
803 store,
804 temporary_store,
805 transaction_kind,
806 rewritten_inputs,
807 tx_ctx,
808 move_vm,
809 gas_charger,
810 protocol_config,
811 metrics.clone(),
812 trace_builder_opt,
813 &mut timings,
814 ),
815 })
816 .and_then(|v| {
817 gas_charger
818 .meter_storage(temporary_store)
819 .map_err(Into::into)
820 .map(|_| v)
821 });
822
823 let checks = check_effects::<Mode>(temporary_store, gas_charger, protocol_config, metrics);
824 let result = result.and_then(|v| checks.map(|()| v));
826
827 if result.is_err() {
828 gas_charger
829 .handle_error(temporary_store)
830 .map_err(|error| (error.into(), BumpOnlyReason::WriteReset))?;
831 }
832 let cost_summary = gas_charger.charge(temporary_store, &result);
833 Ok(ExecutionOutcome {
834 cost_summary,
835 execution_result: result,
836 timings,
837 })
838 }
839
840 fn execute_ptb<Mode: ExecutionMode>(
843 store: &dyn BackingStore,
844 temporary_store: &mut TemporaryStore<'_>,
845 transaction_kind: TransactionKind,
846 rewritten_inputs: Option<Vec<bool>>,
847 tx_ctx: Rc<RefCell<TxContext>>,
848 move_vm: &Arc<MoveRuntime>,
849 gas_charger: &mut GasCharger,
850 protocol_config: &ProtocolConfig,
851 metrics: Arc<ExecutionMetrics>,
852 trace_builder_opt: &mut Option<MoveTraceBuilder>,
853 timings_out: &mut Vec<ExecutionTiming>,
854 ) -> Result<Mode::ExecutionResults, Mode::Error> {
855 let result = match execution_loop::<Mode>(
856 store,
857 temporary_store,
858 transaction_kind,
859 rewritten_inputs,
860 tx_ctx,
861 move_vm,
862 gas_charger,
863 protocol_config,
864 metrics,
865 trace_builder_opt,
866 ) {
867 Ok((v, t)) => {
868 *timings_out = t;
869 Ok(v)
870 }
871 Err((e, t)) => {
872 *timings_out = t;
873 Err(e)
874 }
875 };
876 gas_charger.round_computation(result)
877 }
878
879 fn check_effects<Mode: ExecutionMode>(
880 temporary_store: &mut TemporaryStore<'_>,
881 gas_charger: &mut GasCharger,
882 protocol_config: &ProtocolConfig,
883 metrics: Arc<ExecutionMetrics>,
884 ) -> Result<(), Mode::Error> {
885 let meter = check_meter_limit::<Mode>(
886 temporary_store,
887 gas_charger,
888 protocol_config,
889 metrics.clone(),
890 );
891 let written = check_written_objects_limit::<Mode>(
892 temporary_store,
893 gas_charger,
894 protocol_config,
895 metrics,
896 );
897 let representable = temporary_store
898 .check_accumulator_amounts_representable()
899 .map_err(Into::into);
900 meter.and(written).and(representable)
901 }
902
903 #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
904 fn run_conservation_checks<Mode: ExecutionMode>(
905 temporary_store: &mut TemporaryStore<'_>,
906 gas_charger: &GasCharger,
907 tx_digest: TransactionDigest,
908 move_vm: &Arc<MoveRuntime>,
909 enable_expensive_checks: bool,
910 cost_summary: &GasCostSummary,
911 ) -> Result<(), Mode::Error> {
912 if let Err(conservation_err) = temporary_store.check_conservation_invariants::<Mode>(
913 move_vm,
914 enable_expensive_checks,
915 cost_summary,
916 ) {
917 #[skip_checked_arithmetic]
918 tracing::error!(
919 tx_digest = ?tx_digest,
920 conservation_error = %conservation_err,
921 gas_status = %gas_charger.summary(),
922 "SUI conservation check failed; falling back to the no-op exit (State 2): \
923 dropping all writes and charging nothing",
924 );
925 return Err(conservation_err.into());
926 }
927
928 Ok(())
929 }
930
931 pub(crate) mod legacy {
934 use super::*;
935
936 pub(crate) const ADDRESS_BALANCE_SMASH_FIX_MIN_ACCUMULATOR_VERSION: SequenceNumber =
943 SequenceNumber::from_u64(692949576);
944
945 pub(crate) const ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION:
955 SequenceNumber = SequenceNumber::from_u64(693531074);
956
957 pub(crate) fn should_filter_address_balance_gas_smash(
962 execution_params: &ExecutionOrEarlyError,
963 protocol_config: &ProtocolConfig,
964 ) -> bool {
965 if !head_error_is_insufficient_funds_for_withdraw(execution_params) {
966 return false;
967 }
968 debug_assert!(
969 !protocol_config.early_exit_on_iffw(),
970 "Should not reach gas smashing filtering address balances if IFFW early exit is enabled"
971 );
972 in_test_configuration()
975 || protocol_config.early_exit_on_iffw()
976 || (protocol_config.chain() == Chain::Mainnet
977 && execution_params
978 .accumulator_version()
979 .is_some_and(|v| v >= ADDRESS_BALANCE_SMASH_FIX_MIN_ACCUMULATOR_VERSION))
980 }
981
982 pub(crate) fn should_short_circuit_insufficient_funds(
987 execution_params: &ExecutionOrEarlyError,
988 protocol_config: &ProtocolConfig,
989 ) -> bool {
990 if !execution_params.early_errors().is_some_and(|errors| {
992 errors
993 .iter()
994 .any(|e| matches!(e, ExecutionErrorKind::InsufficientFundsForWithdraw))
995 }) {
996 return false;
997 }
998
999 if in_test_configuration() {
1002 return true;
1003 }
1004
1005 protocol_config.early_exit_on_iffw()
1007 || (protocol_config.chain() == Chain::Mainnet
1008 && execution_params.accumulator_version().is_some_and(|v| {
1009 v >= ADDRESS_BALANCE_SMASH_SHORT_CIRCUIT_MIN_ACCUMULATOR_VERSION
1010 }))
1011 }
1012
1013 pub(crate) fn iffw_filter_address_balance_gas_payments(
1017 gas_data: &mut GasData,
1018 execution_params: &ExecutionOrEarlyError,
1019 protocol_config: &ProtocolConfig,
1020 ) {
1021 if should_short_circuit_insufficient_funds(execution_params, protocol_config) {
1022 return;
1023 }
1024 if should_filter_address_balance_gas_smash(execution_params, protocol_config)
1025 && gas_data.payment.len() > 1
1026 && ParsedDigest::try_from(gas_data.payment[0].2).is_err()
1027 {
1028 gas_data
1029 .payment
1030 .retain(|entry| ParsedDigest::try_from(entry.2).is_err());
1031 }
1032 }
1033
1034 #[allow(clippy::too_many_arguments)]
1036 pub(super) fn execute_transaction_inner<Mode: ExecutionMode>(
1037 store: &dyn BackingStore,
1038 mut temporary_store: TemporaryStore<'_>,
1039 gas_data: GasData,
1040 gas_status: SuiGasStatus,
1041 transaction_kind: TransactionKind,
1042 rewritten_inputs: Option<Vec<bool>>,
1043 transaction_signer: SuiAddress,
1044 transaction_digest: TransactionDigest,
1045 move_vm: &Arc<MoveRuntime>,
1046 epoch_id: &EpochId,
1047 epoch_timestamp_ms: u64,
1048 protocol_config: &ProtocolConfig,
1049 metrics: Arc<ExecutionMetrics>,
1050 enable_expensive_checks: bool,
1051 execution_params: ExecutionOrEarlyError,
1052 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1053 shared_object_refs: Vec<SharedInput>,
1054 mut transaction_dependencies: BTreeSet<TransactionDigest>,
1055 ) -> ExecutionOutput<Mode> {
1056 if should_short_circuit_insufficient_funds(&execution_params, protocol_config) {
1063 assert_reachable!("IFFW short-circuit fired");
1064 temporary_store.ensure_active_inputs_mutated();
1065 transaction_dependencies.remove(&TransactionDigest::genesis_marker());
1066
1067 let execution_error: Mode::Error =
1068 ExecutionError::from_kind(ExecutionErrorKind::InsufficientFundsForWithdraw)
1069 .into();
1070 let status = ExecutionStatus::new_failure(execution_error.to_execution_failure());
1071 let gas_meter = GasCharger::new(
1072 transaction_digest,
1073 PaymentKind::gasless(),
1074 gas_status,
1075 &mut temporary_store,
1076 protocol_config,
1077 );
1078
1079 let gas_coin = gas_meter.gas_coin();
1080 let (inner, effects) = temporary_store.into_effects(
1081 shared_object_refs,
1082 &transaction_digest,
1083 transaction_dependencies,
1084 GasCostSummary::default(),
1085 status,
1086 gas_coin,
1087 *epoch_id,
1088 );
1089
1090 return ExecutionOutput {
1091 inner_store: inner,
1092 gas_status: gas_meter.into_gas_status(),
1093 effects,
1094 timings: vec![],
1095 execution_result: Err(execution_error),
1096 };
1097 }
1098
1099 let sponsor = {
1100 let gas_owner = gas_data.owner;
1101 if gas_owner == transaction_signer {
1102 None
1103 } else {
1104 Some(gas_owner)
1105 }
1106 };
1107 let gas_price = gas_status.gas_price();
1108 let rgp = gas_status.reference_gas_price();
1109
1110 let mut gas_charger = GasCharger::new(
1111 transaction_digest,
1112 legacy_payment_kind(&gas_data, &transaction_kind, protocol_config),
1113 gas_status,
1114 &mut temporary_store,
1115 protocol_config,
1116 );
1117
1118 let tx_ctx = TxContext::new_from_components(
1119 &transaction_signer,
1120 &transaction_digest,
1121 epoch_id,
1122 epoch_timestamp_ms,
1123 rgp,
1124 gas_price,
1125 gas_data.budget,
1126 sponsor,
1127 protocol_config,
1128 );
1129 let tx_ctx = Rc::new(RefCell::new(tx_ctx));
1130
1131 let is_gasless = protocol_config.enable_gasless()
1132 && is_gasless_transaction(&gas_data, &transaction_kind);
1133 let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
1134
1135 let ExecutionOutcome {
1136 cost_summary: gas_cost_summary,
1137 mut execution_result,
1138 timings,
1139 } = execute_transaction::<Mode>(
1140 store,
1141 &mut temporary_store,
1142 transaction_kind,
1143 rewritten_inputs,
1144 &mut gas_charger,
1145 tx_ctx,
1146 move_vm,
1147 protocol_config,
1148 metrics.clone(),
1149 execution_params,
1150 trace_builder_opt,
1151 is_gasless,
1152 );
1153
1154 if let Err(e) = run_invariant_checks::<Mode>(
1157 &mut temporary_store,
1158 &mut gas_charger,
1159 transaction_digest,
1160 move_vm,
1161 protocol_config,
1162 enable_expensive_checks,
1163 &gas_cost_summary,
1164 &transaction_signer,
1165 &sponsor,
1166 is_epoch_change,
1167 execution_result.is_ok(),
1168 ) {
1169 execution_result = Err(e);
1171 }
1172
1173 let status = if let Err(error) = &execution_result {
1174 ExecutionStatus::new_failure(error.to_execution_failure())
1175 } else {
1176 ExecutionStatus::Success
1177 };
1178
1179 #[skip_checked_arithmetic]
1180 trace!(
1181 tx_digest = ?transaction_digest,
1182 computation_gas_cost = gas_cost_summary.computation_cost,
1183 storage_gas_cost = gas_cost_summary.storage_cost,
1184 storage_gas_rebate = gas_cost_summary.storage_rebate,
1185 "Finished execution of transaction with status {:?}",
1186 status
1187 );
1188
1189 transaction_dependencies.remove(&TransactionDigest::genesis_marker());
1193
1194 let gas_coin = gas_charger.gas_coin();
1195 let (inner, effects) = temporary_store.into_effects(
1196 shared_object_refs,
1197 &transaction_digest,
1198 transaction_dependencies,
1199 gas_cost_summary,
1200 status,
1201 gas_coin,
1202 *epoch_id,
1203 );
1204
1205 if !Mode::TRACK_EXECUTION {
1208 update_vm_telemetry_metrics(&metrics, move_vm);
1209 }
1210
1211 ExecutionOutput {
1212 inner_store: inner,
1213 gas_status: gas_charger.into_gas_status(),
1214 effects,
1215 timings,
1216 execution_result,
1217 }
1218 }
1219
1220 #[instrument(name = "tx_execute", level = "debug", skip_all)]
1221 fn execute_transaction<Mode: ExecutionMode>(
1222 store: &dyn BackingStore,
1223 temporary_store: &mut TemporaryStore<'_>,
1224 transaction_kind: TransactionKind,
1225 rewritten_inputs: Option<Vec<bool>>,
1226 gas_charger: &mut GasCharger,
1227 tx_ctx: Rc<RefCell<TxContext>>,
1228 move_vm: &Arc<MoveRuntime>,
1229 protocol_config: &ProtocolConfig,
1230 metrics: Arc<ExecutionMetrics>,
1231 execution_params: ExecutionOrEarlyError,
1232 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1233 is_gasless: bool,
1234 ) -> ExecutionOutcome<Mode> {
1235 debug_assert!(
1237 gas_charger.no_charges(),
1238 "No gas charges must be applied yet"
1239 );
1240
1241 let withdrawal_reservations =
1242 if is_gasless && protocol_config.gasless_verify_remaining_balance() {
1243 gasless_withdrawal_reservations(&transaction_kind, &tx_ctx.borrow())
1244 } else {
1245 None
1246 };
1247
1248 let result = gas_charger.charge_input_objects_legacy(temporary_store);
1251
1252 let result: ResultWithTimings<Mode::ExecutionResults, Mode::Error> =
1253 result.map_err(|e| (e.into(), vec![])).and_then(
1254 |()| -> ResultWithTimings<Mode::ExecutionResults, Mode::Error> {
1255 let mut execution_result: ResultWithTimings<
1256 Mode::ExecutionResults,
1257 Mode::Error,
1258 > = match execution_params.into_early_errors() {
1259 Some(early_execution_errors) => {
1260 Err((Mode::Error::from_kind(early_execution_errors.head), vec![]))
1261 }
1262 None => execution_loop::<Mode>(
1263 store,
1264 temporary_store,
1265 transaction_kind,
1266 rewritten_inputs,
1267 tx_ctx,
1268 move_vm,
1269 gas_charger,
1270 protocol_config,
1271 metrics.clone(),
1272 trace_builder_opt,
1273 ),
1274 };
1275
1276 let meter_check = check_meter_limit::<Mode>(
1277 temporary_store,
1278 gas_charger,
1279 protocol_config,
1280 metrics.clone(),
1281 );
1282 if let Err(e) = meter_check {
1283 execution_result = Err((e, vec![]));
1284 }
1285
1286 if execution_result.is_ok() {
1287 let gas_check = check_written_objects_limit::<Mode>(
1288 temporary_store,
1289 gas_charger,
1290 protocol_config,
1291 metrics,
1292 );
1293 if let Err(e) = gas_check {
1294 execution_result = Err((e, vec![]));
1295 }
1296 }
1297
1298 execution_result
1299 },
1300 );
1301
1302 let (mut result, timings) = match result {
1303 Ok((r, t)) => (Ok(r), t),
1304 Err((e, t)) => (Err(e), t),
1305 };
1306 if is_gasless
1307 && result.is_ok()
1308 && let Err(msg) = temporary_store
1309 .check_gasless_execution_requirements_with_reservations(
1310 withdrawal_reservations.as_ref(),
1311 )
1312 {
1313 result = Err(Mode::Error::new_with_source(
1314 ExecutionErrorKind::InsufficientGas,
1315 msg,
1316 ));
1317 }
1318
1319 if result.is_ok()
1334 && let Err(e) = temporary_store.check_accumulator_amounts_representable()
1335 {
1336 result = Err(e.into());
1337 }
1338
1339 let cost_summary =
1340 gas_charger.legacy_charge_gas(temporary_store, protocol_config, &mut result);
1341 temporary_store
1349 .conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
1350
1351 ExecutionOutcome {
1352 cost_summary,
1353 execution_result: result,
1354 timings,
1355 }
1356 }
1357
1358 fn gasless_withdrawal_reservations(
1359 transaction_kind: &TransactionKind,
1360 tx_ctx: &TxContext,
1361 ) -> Option<BTreeMap<(SuiAddress, TypeTag), u64>> {
1362 let TransactionKind::ProgrammableTransaction(pt) = transaction_kind else {
1363 debug_fatal!("Gasless transaction must be a ProgrammableTransaction");
1364 return None;
1365 };
1366 let sender = tx_ctx.sender();
1367 let mut reservations = BTreeMap::<(SuiAddress, TypeTag), u64>::new();
1368 for input in &pt.inputs {
1369 let CallArg::FundsWithdrawal(fw) = input else {
1370 continue;
1371 };
1372 let Some(coin_type) = fw.type_arg.get_balance_type_param() else {
1373 debug_fatal!("expected Balance type for withdrawal");
1374 continue;
1375 };
1376 let owner = match fw.withdraw_from {
1377 WithdrawFrom::Sender => sender,
1378 WithdrawFrom::Sponsor => {
1379 debug_fatal!(
1380 "WithdrawFrom::Sponsor is not expected in gasless transactions"
1381 );
1382 tx_ctx.sponsor().unwrap_or(sender)
1383 }
1384 WithdrawFrom::SenderAllowance { funder, .. } => funder,
1385 };
1386 let Reservation::MaxAmountU64(amount) = fw.reservation;
1387 let entry = reservations.entry((owner, coin_type)).or_insert(0);
1388 *entry = entry.saturating_add(amount);
1389 }
1390 Some(reservations)
1391 }
1392
1393 #[allow(clippy::too_many_arguments)]
1406 fn run_invariant_checks<Mode: ExecutionMode>(
1407 temporary_store: &mut TemporaryStore<'_>,
1408 gas_charger: &mut GasCharger,
1409 tx_digest: TransactionDigest,
1410 move_vm: &Arc<MoveRuntime>,
1411 protocol_config: &ProtocolConfig,
1412 enable_expensive_checks: bool,
1413 cost_summary: &GasCostSummary,
1414 sender: &SuiAddress,
1415 sponsor: &Option<SuiAddress>,
1416 is_epoch_change: bool,
1417 execution_succeeded: bool,
1418 ) -> Result<(), Mode::Error> {
1419 let conservation = run_conservation_checks::<Mode>(
1420 temporary_store,
1421 gas_charger,
1422 tx_digest,
1423 move_vm,
1424 protocol_config,
1425 enable_expensive_checks,
1426 cost_summary,
1427 );
1428 if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
1429 temporary_store
1430 .check_ownership_invariants(sender, sponsor, gas_charger, is_epoch_change)
1431 .unwrap()
1432 } if execution_succeeded {
1435 temporary_store.check_published_packages()?;
1436 }
1437 conservation
1438 }
1439
1440 #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
1447 fn run_conservation_checks<Mode: ExecutionMode>(
1448 temporary_store: &mut TemporaryStore<'_>,
1449 gas_charger: &mut GasCharger,
1450 tx_digest: TransactionDigest,
1451 move_vm: &Arc<MoveRuntime>,
1452 protocol_config: &ProtocolConfig,
1453 enable_expensive_checks: bool,
1454 cost_summary: &GasCostSummary,
1455 ) -> Result<(), Mode::Error> {
1456 let Err(conservation_err) = temporary_store.check_conservation_invariants::<Mode>(
1457 move_vm,
1458 enable_expensive_checks,
1459 cost_summary,
1460 ) else {
1461 return Ok(());
1462 };
1463
1464 let mut result: Result<(), Mode::Error> = Err(conservation_err.into());
1468 gas_charger.reset(temporary_store);
1469 gas_charger.legacy_charge_gas(temporary_store, protocol_config, &mut result);
1470 if let Err(recovery_err) = temporary_store.check_conservation_invariants::<Mode>(
1471 move_vm,
1472 enable_expensive_checks,
1473 cost_summary,
1474 ) {
1475 panic!(
1479 "SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
1480 tx_digest,
1481 recovery_err,
1482 gas_charger.summary()
1483 )
1484 }
1485 result
1486 }
1487 }
1488
1489 #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
1490 fn check_meter_limit<Mode: ExecutionMode>(
1491 temporary_store: &mut TemporaryStore<'_>,
1492 gas_charger: &mut GasCharger,
1493 protocol_config: &ProtocolConfig,
1494 metrics: Arc<ExecutionMetrics>,
1495 ) -> Result<(), Mode::Error> {
1496 let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
1497
1498 match check_limit_by_meter!(
1502 !gas_charger.is_unmetered(),
1503 effects_estimated_size,
1504 protocol_config.max_serialized_tx_effects_size_bytes(),
1505 protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
1506 metrics.limits_metrics.excessive_estimated_effects_size
1507 ) {
1508 LimitThresholdCrossed::None => Ok(()),
1509 LimitThresholdCrossed::Soft(_, limit) => {
1510 warn!(
1511 effects_estimated_size = effects_estimated_size,
1512 soft_limit = limit,
1513 "Estimated transaction effects size crossed soft limit",
1514 );
1515 Ok(())
1516 }
1517 LimitThresholdCrossed::Hard(_, lim) => Err(Mode::Error::new_with_source(
1518 ExecutionErrorKind::EffectsTooLarge {
1519 current_size: effects_estimated_size as u64,
1520 max_size: lim as u64,
1521 },
1522 "Transaction effects are too large",
1523 )),
1524 }
1525 }
1526
1527 #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
1528 fn check_written_objects_limit<Mode: ExecutionMode>(
1529 temporary_store: &mut TemporaryStore<'_>,
1530 gas_charger: &mut GasCharger,
1531 protocol_config: &ProtocolConfig,
1532 metrics: Arc<ExecutionMetrics>,
1533 ) -> Result<(), Mode::Error> {
1534 if let (Some(normal_lim), Some(system_lim)) = (
1535 protocol_config.max_size_written_objects_as_option(),
1536 protocol_config.max_size_written_objects_system_tx_as_option(),
1537 ) {
1538 let written_objects_size = temporary_store.written_objects_size();
1539
1540 match check_limit_by_meter!(
1541 !gas_charger.is_unmetered(),
1542 written_objects_size,
1543 normal_lim,
1544 system_lim,
1545 metrics.limits_metrics.excessive_written_objects_size
1546 ) {
1547 LimitThresholdCrossed::None => (),
1548 LimitThresholdCrossed::Soft(_, limit) => {
1549 warn!(
1550 written_objects_size = written_objects_size,
1551 soft_limit = limit,
1552 "Written objects size crossed soft limit",
1553 )
1554 }
1555 LimitThresholdCrossed::Hard(_, lim) => {
1556 return Err(Mode::Error::new_with_source(
1557 ExecutionErrorKind::WrittenObjectsTooLarge {
1558 current_size: written_objects_size as u64,
1559 max_size: lim as u64,
1560 },
1561 "Written objects size crossed hard limit",
1562 ));
1563 }
1564 };
1565 }
1566
1567 Ok(())
1568 }
1569
1570 #[instrument(level = "debug", skip_all)]
1571 fn execution_loop<Mode: ExecutionMode>(
1572 store: &dyn BackingStore,
1573 temporary_store: &mut TemporaryStore<'_>,
1574 transaction_kind: TransactionKind,
1575 rewritten_inputs: Option<Vec<bool>>,
1576 tx_ctx: Rc<RefCell<TxContext>>,
1577 move_vm: &Arc<MoveRuntime>,
1578 gas_charger: &mut GasCharger,
1579 protocol_config: &ProtocolConfig,
1580 metrics: Arc<ExecutionMetrics>,
1581 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1582 ) -> ResultWithTimings<Mode::ExecutionResults, Mode::Error> {
1583 let result = match transaction_kind {
1584 TransactionKind::ChangeEpoch(change_epoch) => {
1585 let builder = ProgrammableTransactionBuilder::new();
1586 advance_epoch::<Mode>(
1587 builder,
1588 change_epoch,
1589 temporary_store,
1590 store,
1591 tx_ctx,
1592 move_vm,
1593 gas_charger,
1594 protocol_config,
1595 metrics,
1596 trace_builder_opt,
1597 )
1598 .map_err(|e| (e, vec![]))?;
1599 Ok((Mode::empty_results(), vec![]))
1600 }
1601 TransactionKind::Genesis(GenesisTransaction { objects }) => {
1602 if tx_ctx.borrow().epoch() != 0 {
1603 panic!("BUG: Genesis Transactions can only be executed in epoch 0");
1604 }
1605
1606 for genesis_object in objects {
1607 match genesis_object {
1608 sui_types::transaction::GenesisObject::RawObject { data, owner } => {
1609 let object = ObjectInner {
1610 data,
1611 owner,
1612 previous_transaction: tx_ctx.borrow().digest(),
1613 storage_rebate: 0,
1614 };
1615 temporary_store.create_object(object.into());
1616 }
1617 }
1618 }
1619 Ok((Mode::empty_results(), vec![]))
1620 }
1621 TransactionKind::ConsensusCommitPrologue(prologue) => {
1622 setup_consensus_commit::<Mode>(
1623 prologue.commit_timestamp_ms,
1624 temporary_store,
1625 store,
1626 tx_ctx,
1627 move_vm,
1628 gas_charger,
1629 protocol_config,
1630 metrics,
1631 trace_builder_opt,
1632 )
1633 .expect("ConsensusCommitPrologue cannot fail");
1634 Ok((Mode::empty_results(), vec![]))
1635 }
1636 TransactionKind::ConsensusCommitPrologueV2(prologue) => {
1637 setup_consensus_commit::<Mode>(
1638 prologue.commit_timestamp_ms,
1639 temporary_store,
1640 store,
1641 tx_ctx,
1642 move_vm,
1643 gas_charger,
1644 protocol_config,
1645 metrics,
1646 trace_builder_opt,
1647 )
1648 .expect("ConsensusCommitPrologueV2 cannot fail");
1649 Ok((Mode::empty_results(), vec![]))
1650 }
1651 TransactionKind::ConsensusCommitPrologueV3(prologue) => {
1652 setup_consensus_commit::<Mode>(
1653 prologue.commit_timestamp_ms,
1654 temporary_store,
1655 store,
1656 tx_ctx,
1657 move_vm,
1658 gas_charger,
1659 protocol_config,
1660 metrics,
1661 trace_builder_opt,
1662 )
1663 .expect("ConsensusCommitPrologueV3 cannot fail");
1664 Ok((Mode::empty_results(), vec![]))
1665 }
1666 TransactionKind::ConsensusCommitPrologueV4(prologue) => {
1667 setup_consensus_commit::<Mode>(
1668 prologue.commit_timestamp_ms,
1669 temporary_store,
1670 store,
1671 tx_ctx,
1672 move_vm,
1673 gas_charger,
1674 protocol_config,
1675 metrics,
1676 trace_builder_opt,
1677 )
1678 .expect("ConsensusCommitPrologue cannot fail");
1679 Ok((Mode::empty_results(), vec![]))
1680 }
1681 TransactionKind::ProgrammableTransaction(pt) => SPT::execute::<Mode>(
1682 protocol_config,
1683 metrics,
1684 move_vm,
1685 temporary_store,
1686 store,
1687 tx_ctx,
1688 gas_charger,
1689 rewritten_inputs,
1690 pt,
1691 trace_builder_opt,
1692 ),
1693 TransactionKind::ProgrammableSystemTransaction(pt) => {
1694 SPT::execute::<execution_mode::System<Mode::Error>>(
1695 protocol_config,
1696 metrics,
1697 move_vm,
1698 temporary_store,
1699 store,
1700 tx_ctx,
1701 gas_charger,
1702 None,
1703 pt,
1704 trace_builder_opt,
1705 )
1706 .map_err(|(e, _)| (e, vec![]))?;
1707 Ok((Mode::empty_results(), vec![]))
1708 }
1709 TransactionKind::EndOfEpochTransaction(txns) => {
1710 let mut builder = ProgrammableTransactionBuilder::new();
1711 let len = txns.len();
1712 for (i, tx) in txns.into_iter().enumerate() {
1713 match tx {
1714 EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
1715 assert_eq!(i, len - 1);
1716 advance_epoch::<Mode>(
1717 builder,
1718 change_epoch,
1719 temporary_store,
1720 store,
1721 tx_ctx,
1722 move_vm,
1723 gas_charger,
1724 protocol_config,
1725 metrics,
1726 trace_builder_opt,
1727 )
1728 .map_err(|e| (e, vec![]))?;
1729 return Ok((Mode::empty_results(), vec![]));
1730 }
1731 EndOfEpochTransactionKind::AuthenticatorStateCreate => {
1732 assert!(protocol_config.enable_jwk_consensus_updates());
1733 builder = setup_authenticator_state_create(builder);
1734 }
1735 EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
1736 assert!(protocol_config.enable_jwk_consensus_updates());
1737
1738 builder = setup_authenticator_state_expire(builder, expire);
1741 }
1742 EndOfEpochTransactionKind::RandomnessStateCreate => {
1743 assert!(protocol_config.random_beacon());
1744 builder = setup_randomness_state_create(builder);
1745 }
1746 EndOfEpochTransactionKind::DenyListStateCreate => {
1747 assert!(protocol_config.enable_coin_deny_list());
1748 builder = setup_coin_deny_list_state_create(builder);
1749 }
1750 EndOfEpochTransactionKind::BridgeStateCreate(chain_id) => {
1751 assert!(protocol_config.bridge());
1752 builder = setup_bridge_create(builder, chain_id)
1753 }
1754 EndOfEpochTransactionKind::BridgeCommitteeInit(bridge_shared_version) => {
1755 assert!(protocol_config.bridge());
1756 assert!(protocol_config.should_try_to_finalize_bridge_committee());
1757 builder = setup_bridge_committee_update(builder, bridge_shared_version)
1758 }
1759 EndOfEpochTransactionKind::StoreExecutionTimeObservations(estimates) => {
1760 if let PerObjectCongestionControlMode::ExecutionTimeEstimate(params) =
1761 protocol_config.per_object_congestion_control_mode()
1762 {
1763 let chunk_size = params
1764 .observations_chunk_size
1765 .expect("observation chunking is enabled at all protocol versions handled by this execution layer");
1766 builder = setup_store_execution_time_estimates(
1767 builder,
1768 estimates,
1769 chunk_size as usize,
1770 );
1771 }
1772 }
1773 EndOfEpochTransactionKind::AccumulatorRootCreate => {
1774 assert!(protocol_config.create_root_accumulator_object());
1775 builder = setup_accumulator_root_create(builder);
1776 }
1777 EndOfEpochTransactionKind::WriteAccumulatorStorageCost(
1778 write_storage_cost,
1779 ) => {
1780 assert!(protocol_config.enable_accumulators());
1781 builder =
1782 setup_write_accumulator_storage_cost(builder, &write_storage_cost);
1783 }
1784 EndOfEpochTransactionKind::CoinRegistryCreate => {
1785 assert!(protocol_config.enable_coin_registry());
1786 builder = setup_coin_registry_create(builder);
1787 }
1788 EndOfEpochTransactionKind::DisplayRegistryCreate => {
1789 assert!(protocol_config.enable_display_registry());
1790 builder = setup_display_registry_create(builder);
1791 }
1792 EndOfEpochTransactionKind::AddressAliasStateCreate => {
1793 assert!(protocol_config.address_aliases());
1794 builder = setup_address_alias_state_create(builder);
1795 }
1796 EndOfEpochTransactionKind::ForwardingAddressRegistryCreate => {
1797 assert!(protocol_config.create_forwarding_address_registry());
1798 builder = setup_forwarding_address_registry_create(builder);
1799 }
1800 }
1801 }
1802 unreachable!(
1803 "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
1804 )
1805 }
1806 TransactionKind::AuthenticatorStateUpdate(auth_state_update) => {
1807 setup_authenticator_state_update::<Mode>(
1808 auth_state_update,
1809 temporary_store,
1810 store,
1811 tx_ctx,
1812 move_vm,
1813 gas_charger,
1814 protocol_config,
1815 metrics,
1816 trace_builder_opt,
1817 )
1818 .map_err(|e| (e, vec![]))?;
1819 Ok((Mode::empty_results(), vec![]))
1820 }
1821 TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
1822 setup_randomness_state_update::<Mode>(
1823 randomness_state_update,
1824 temporary_store,
1825 store,
1826 tx_ctx,
1827 move_vm,
1828 gas_charger,
1829 protocol_config,
1830 metrics,
1831 trace_builder_opt,
1832 )
1833 .map_err(|e| (e, vec![]))?;
1834 Ok((Mode::empty_results(), vec![]))
1835 }
1836 }?;
1837 temporary_store
1838 .check_execution_results_consistency::<Mode>()
1839 .map_err(|e| (e, vec![]))?;
1840 Ok(result)
1841 }
1842
1843 fn mint_epoch_rewards_in_pt(
1844 builder: &mut ProgrammableTransactionBuilder,
1845 params: &AdvanceEpochParams,
1846 ) -> (Argument, Argument) {
1847 let storage_charge_arg = builder
1849 .input(CallArg::Pure(
1850 bcs::to_bytes(¶ms.storage_charge).unwrap(),
1851 ))
1852 .unwrap();
1853 let storage_rewards = builder.programmable_move_call(
1854 SUI_FRAMEWORK_PACKAGE_ID,
1855 BALANCE_MODULE_NAME.to_owned(),
1856 BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
1857 vec![GAS::type_tag()],
1858 vec![storage_charge_arg],
1859 );
1860
1861 let computation_charge_arg = builder
1863 .input(CallArg::Pure(
1864 bcs::to_bytes(¶ms.computation_charge).unwrap(),
1865 ))
1866 .unwrap();
1867 let computation_rewards = builder.programmable_move_call(
1868 SUI_FRAMEWORK_PACKAGE_ID,
1869 BALANCE_MODULE_NAME.to_owned(),
1870 BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
1871 vec![GAS::type_tag()],
1872 vec![computation_charge_arg],
1873 );
1874 (storage_rewards, computation_rewards)
1875 }
1876
1877 pub fn construct_advance_epoch_pt<Mode: ExecutionMode>(
1878 mut builder: ProgrammableTransactionBuilder,
1879 params: &AdvanceEpochParams,
1880 ) -> Result<ProgrammableTransaction, Mode::Error> {
1881 let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
1883
1884 let mut arguments = vec![storage_rewards, computation_rewards];
1886 let call_arg_arguments = vec![
1887 CallArg::SUI_SYSTEM_MUT,
1888 CallArg::Pure(bcs::to_bytes(¶ms.epoch).unwrap()),
1889 CallArg::Pure(bcs::to_bytes(¶ms.next_protocol_version.as_u64()).unwrap()),
1890 CallArg::Pure(bcs::to_bytes(¶ms.storage_rebate).unwrap()),
1891 CallArg::Pure(bcs::to_bytes(¶ms.non_refundable_storage_fee).unwrap()),
1892 CallArg::Pure(bcs::to_bytes(¶ms.storage_fund_reinvest_rate).unwrap()),
1893 CallArg::Pure(bcs::to_bytes(¶ms.reward_slashing_rate).unwrap()),
1894 CallArg::Pure(bcs::to_bytes(¶ms.epoch_start_timestamp_ms).unwrap()),
1895 ]
1896 .into_iter()
1897 .map(|a| builder.input(a))
1898 .collect::<Result<_, _>>();
1899
1900 assert_invariant!(
1901 call_arg_arguments.is_ok(),
1902 "Unable to generate args for advance_epoch transaction!"
1903 );
1904
1905 arguments.append(&mut call_arg_arguments.unwrap());
1906
1907 info!("Call arguments to advance_epoch transaction: {:?}", params);
1908
1909 let storage_rebates = builder.programmable_move_call(
1910 SUI_SYSTEM_PACKAGE_ID,
1911 SUI_SYSTEM_MODULE_NAME.to_owned(),
1912 ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
1913 vec![],
1914 arguments,
1915 );
1916
1917 builder.programmable_move_call(
1919 SUI_FRAMEWORK_PACKAGE_ID,
1920 BALANCE_MODULE_NAME.to_owned(),
1921 BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
1922 vec![GAS::type_tag()],
1923 vec![storage_rebates],
1924 );
1925 Ok(builder.finish())
1926 }
1927
1928 pub fn construct_advance_epoch_safe_mode_pt(
1929 params: &AdvanceEpochParams,
1930 ) -> Result<ProgrammableTransaction, ExecutionError> {
1931 let mut builder = ProgrammableTransactionBuilder::new();
1932 let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
1934
1935 let mut arguments = vec![storage_rewards, computation_rewards];
1937
1938 let mut args = vec![
1939 CallArg::SUI_SYSTEM_MUT,
1940 CallArg::Pure(bcs::to_bytes(¶ms.epoch).unwrap()),
1941 CallArg::Pure(bcs::to_bytes(¶ms.next_protocol_version.as_u64()).unwrap()),
1942 CallArg::Pure(bcs::to_bytes(¶ms.storage_rebate).unwrap()),
1943 CallArg::Pure(bcs::to_bytes(¶ms.non_refundable_storage_fee).unwrap()),
1944 ];
1945
1946 args.push(CallArg::Pure(
1947 bcs::to_bytes(¶ms.epoch_start_timestamp_ms).unwrap(),
1948 ));
1949
1950 let call_arg_arguments = args
1951 .into_iter()
1952 .map(|a| builder.input(a))
1953 .collect::<Result<_, _>>();
1954
1955 assert_invariant!(
1956 call_arg_arguments.is_ok(),
1957 "Unable to generate args for advance_epoch transaction!"
1958 );
1959
1960 arguments.append(&mut call_arg_arguments.unwrap());
1961
1962 info!("Call arguments to advance_epoch transaction: {:?}", params);
1963
1964 builder.programmable_move_call(
1965 SUI_SYSTEM_PACKAGE_ID,
1966 SUI_SYSTEM_MODULE_NAME.to_owned(),
1967 ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
1968 vec![],
1969 arguments,
1970 );
1971
1972 Ok(builder.finish())
1973 }
1974
1975 fn advance_epoch<Mode: ExecutionMode>(
1976 builder: ProgrammableTransactionBuilder,
1977 change_epoch: ChangeEpoch,
1978 temporary_store: &mut TemporaryStore<'_>,
1979 store: &dyn BackingStore,
1980 tx_ctx: Rc<RefCell<TxContext>>,
1981 move_vm: &Arc<MoveRuntime>,
1982 gas_charger: &mut GasCharger,
1983 protocol_config: &ProtocolConfig,
1984 metrics: Arc<ExecutionMetrics>,
1985 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1986 ) -> Result<(), Mode::Error> {
1987 let params = AdvanceEpochParams {
1988 epoch: change_epoch.epoch,
1989 next_protocol_version: change_epoch.protocol_version,
1990 storage_charge: change_epoch.storage_charge,
1991 computation_charge: change_epoch.computation_charge,
1992 storage_rebate: change_epoch.storage_rebate,
1993 non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
1994 storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
1995 reward_slashing_rate: protocol_config.reward_slashing_rate(),
1996 epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
1997 };
1998 let advance_epoch_pt = construct_advance_epoch_pt::<Mode>(builder, ¶ms)?;
1999 let result = SPT::execute::<execution_mode::System<Mode::Error>>(
2000 protocol_config,
2001 metrics.clone(),
2002 move_vm,
2003 temporary_store,
2004 store,
2005 tx_ctx.clone(),
2006 gas_charger,
2007 None,
2008 advance_epoch_pt,
2009 trace_builder_opt,
2010 );
2011
2012 #[cfg(msim)]
2013 let result = maybe_modify_result_for(result, change_epoch.epoch);
2014
2015 if let Err(err) = &result {
2016 tracing::error!(
2017 "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
2018 err.0,
2019 temporary_store.objects(),
2020 change_epoch,
2021 );
2022 temporary_store.drop_writes();
2023 gas_charger.reset_storage_cost_and_rebate();
2025
2026 temporary_store.advance_epoch_safe_mode(¶ms, protocol_config);
2027 }
2028
2029 let new_vm = new_move_runtime(
2030 all_natives(true, protocol_config),
2031 protocol_config,
2032 )
2033 .expect("Failed to create new MoveRuntime");
2034 process_system_packages(
2035 change_epoch,
2036 temporary_store,
2037 store,
2038 tx_ctx,
2039 &new_vm,
2040 gas_charger,
2041 protocol_config,
2042 metrics,
2043 trace_builder_opt,
2044 );
2045 Ok(())
2046 }
2047
2048 fn process_system_packages(
2049 change_epoch: ChangeEpoch,
2050 temporary_store: &mut TemporaryStore<'_>,
2051 store: &dyn BackingStore,
2052 tx_ctx: Rc<RefCell<TxContext>>,
2053 move_vm: &MoveRuntime,
2054 gas_charger: &mut GasCharger,
2055 protocol_config: &ProtocolConfig,
2056 metrics: Arc<ExecutionMetrics>,
2057 trace_builder_opt: &mut Option<MoveTraceBuilder>,
2058 ) {
2059 let digest = tx_ctx.borrow().digest();
2060 let binary_config = protocol_config.binary_config(None);
2061 for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
2062 let deserialized_modules: Vec<_> = modules
2063 .iter()
2064 .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
2065 .collect();
2066
2067 if version == OBJECT_START_VERSION {
2068 let package_id = deserialized_modules.first().unwrap().address();
2069 info!("adding new system package {package_id}");
2070
2071 let publish_pt = {
2072 let mut b = ProgrammableTransactionBuilder::new();
2073 b.command(Command::Publish(modules, dependencies));
2074 b.finish()
2075 };
2076
2077 SPT::execute::<execution_mode::System>(
2078 protocol_config,
2079 metrics.clone(),
2080 move_vm,
2081 temporary_store,
2082 store,
2083 tx_ctx.clone(),
2084 gas_charger,
2085 None,
2086 publish_pt,
2087 trace_builder_opt,
2088 )
2089 .map_err(|(e, _)| e)
2090 .expect("System Package Publish must succeed");
2091 } else {
2092 let mut new_package = Object::new_system_package(
2093 &deserialized_modules,
2094 version,
2095 dependencies,
2096 digest,
2097 );
2098
2099 info!(
2100 "upgraded system package {:?}",
2101 new_package.compute_object_reference()
2102 );
2103
2104 new_package
2107 .data
2108 .try_as_package_mut()
2109 .unwrap()
2110 .decrement_version();
2111
2112 temporary_store.upgrade_system_package(new_package);
2114 }
2115 }
2116 }
2117
2118 fn setup_consensus_commit<Mode: ExecutionMode>(
2123 consensus_commit_timestamp_ms: CheckpointTimestamp,
2124 temporary_store: &mut TemporaryStore<'_>,
2125 store: &dyn BackingStore,
2126 tx_ctx: Rc<RefCell<TxContext>>,
2127 move_vm: &Arc<MoveRuntime>,
2128 gas_charger: &mut GasCharger,
2129 protocol_config: &ProtocolConfig,
2130 metrics: Arc<ExecutionMetrics>,
2131 trace_builder_opt: &mut Option<MoveTraceBuilder>,
2132 ) -> Result<(), Mode::Error> {
2133 let pt = {
2134 let mut builder = ProgrammableTransactionBuilder::new();
2135 let res = builder.move_call(
2136 SUI_FRAMEWORK_ADDRESS.into(),
2137 CLOCK_MODULE_NAME.to_owned(),
2138 CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
2139 vec![],
2140 vec![
2141 CallArg::CLOCK_MUT,
2142 CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
2143 ],
2144 );
2145 assert_invariant!(
2146 res.is_ok(),
2147 "Unable to generate consensus_commit_prologue transaction!"
2148 );
2149 builder.finish()
2150 };
2151 SPT::execute::<execution_mode::System<Mode::Error>>(
2152 protocol_config,
2153 metrics,
2154 move_vm,
2155 temporary_store,
2156 store,
2157 tx_ctx,
2158 gas_charger,
2159 None,
2160 pt,
2161 trace_builder_opt,
2162 )
2163 .map_err(|(e, _)| e)?;
2164 Ok(())
2165 }
2166
2167 fn setup_authenticator_state_create(
2168 mut builder: ProgrammableTransactionBuilder,
2169 ) -> ProgrammableTransactionBuilder {
2170 builder
2171 .move_call(
2172 SUI_FRAMEWORK_ADDRESS.into(),
2173 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
2174 AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
2175 vec![],
2176 vec![],
2177 )
2178 .expect("Unable to generate authenticator_state_create transaction!");
2179 builder
2180 }
2181
2182 fn setup_randomness_state_create(
2183 mut builder: ProgrammableTransactionBuilder,
2184 ) -> ProgrammableTransactionBuilder {
2185 builder
2186 .move_call(
2187 SUI_FRAMEWORK_ADDRESS.into(),
2188 RANDOMNESS_MODULE_NAME.to_owned(),
2189 RANDOMNESS_STATE_CREATE_FUNCTION_NAME.to_owned(),
2190 vec![],
2191 vec![],
2192 )
2193 .expect("Unable to generate randomness_state_create transaction!");
2194 builder
2195 }
2196
2197 fn setup_bridge_create(
2198 mut builder: ProgrammableTransactionBuilder,
2199 chain_id: ChainIdentifier,
2200 ) -> ProgrammableTransactionBuilder {
2201 let bridge_uid = builder
2202 .input(CallArg::Pure(UID::new(SUI_BRIDGE_OBJECT_ID).to_bcs_bytes()))
2203 .expect("Unable to create Bridge object UID!");
2204
2205 let bridge_chain_id = if chain_id == get_mainnet_chain_identifier() {
2206 BridgeChainId::SuiMainnet as u8
2207 } else if chain_id == get_testnet_chain_identifier() {
2208 BridgeChainId::SuiTestnet as u8
2209 } else {
2210 BridgeChainId::SuiCustom as u8
2212 };
2213
2214 let bridge_chain_id = builder.pure(bridge_chain_id).unwrap();
2215 builder.programmable_move_call(
2216 BRIDGE_ADDRESS.into(),
2217 BRIDGE_MODULE_NAME.to_owned(),
2218 BRIDGE_CREATE_FUNCTION_NAME.to_owned(),
2219 vec![],
2220 vec![bridge_uid, bridge_chain_id],
2221 );
2222 builder
2223 }
2224
2225 fn setup_bridge_committee_update(
2226 mut builder: ProgrammableTransactionBuilder,
2227 bridge_shared_version: SequenceNumber,
2228 ) -> ProgrammableTransactionBuilder {
2229 let bridge = builder
2230 .obj(ObjectArg::SharedObject {
2231 id: SUI_BRIDGE_OBJECT_ID,
2232 initial_shared_version: bridge_shared_version,
2233 mutability: sui_types::transaction::SharedObjectMutability::Mutable,
2234 })
2235 .expect("Unable to create Bridge object arg!");
2236 let system_state = builder
2237 .obj(ObjectArg::SUI_SYSTEM_MUT)
2238 .expect("Unable to create System State object arg!");
2239
2240 let voting_power = builder.programmable_move_call(
2241 SUI_SYSTEM_PACKAGE_ID,
2242 SUI_SYSTEM_MODULE_NAME.to_owned(),
2243 ident_str!("validator_voting_powers").to_owned(),
2244 vec![],
2245 vec![system_state],
2246 );
2247
2248 let min_stake_participation_percentage = builder
2251 .input(CallArg::Pure(
2252 bcs::to_bytes(&BRIDGE_COMMITTEE_MINIMAL_VOTING_POWER).unwrap(),
2253 ))
2254 .unwrap();
2255
2256 builder.programmable_move_call(
2257 BRIDGE_ADDRESS.into(),
2258 BRIDGE_MODULE_NAME.to_owned(),
2259 BRIDGE_INIT_COMMITTEE_FUNCTION_NAME.to_owned(),
2260 vec![],
2261 vec![bridge, voting_power, min_stake_participation_percentage],
2262 );
2263 builder
2264 }
2265
2266 fn setup_authenticator_state_update<Mode: ExecutionMode>(
2267 update: AuthenticatorStateUpdate,
2268 temporary_store: &mut TemporaryStore<'_>,
2269 store: &dyn BackingStore,
2270 tx_ctx: Rc<RefCell<TxContext>>,
2271 move_vm: &Arc<MoveRuntime>,
2272 gas_charger: &mut GasCharger,
2273 protocol_config: &ProtocolConfig,
2274 metrics: Arc<ExecutionMetrics>,
2275 trace_builder_opt: &mut Option<MoveTraceBuilder>,
2276 ) -> Result<(), Mode::Error> {
2277 let pt = {
2278 let mut builder = ProgrammableTransactionBuilder::new();
2279 let res = builder.move_call(
2280 SUI_FRAMEWORK_ADDRESS.into(),
2281 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
2282 AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
2283 vec![],
2284 vec![
2285 CallArg::Object(ObjectArg::SharedObject {
2286 id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
2287 initial_shared_version: update.authenticator_obj_initial_shared_version,
2288 mutability: sui_types::transaction::SharedObjectMutability::Mutable,
2289 }),
2290 CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
2291 ],
2292 );
2293 assert_invariant!(
2294 res.is_ok(),
2295 "Unable to generate authenticator_state_update transaction!"
2296 );
2297 builder.finish()
2298 };
2299 SPT::execute::<execution_mode::System<Mode::Error>>(
2300 protocol_config,
2301 metrics,
2302 move_vm,
2303 temporary_store,
2304 store,
2305 tx_ctx,
2306 gas_charger,
2307 None,
2308 pt,
2309 trace_builder_opt,
2310 )
2311 .map_err(|(e, _)| e)?;
2312 Ok(())
2313 }
2314
2315 fn setup_authenticator_state_expire(
2316 mut builder: ProgrammableTransactionBuilder,
2317 expire: AuthenticatorStateExpire,
2318 ) -> ProgrammableTransactionBuilder {
2319 builder
2320 .move_call(
2321 SUI_FRAMEWORK_ADDRESS.into(),
2322 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
2323 AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
2324 vec![],
2325 vec![
2326 CallArg::Object(ObjectArg::SharedObject {
2327 id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
2328 initial_shared_version: expire.authenticator_obj_initial_shared_version,
2329 mutability: sui_types::transaction::SharedObjectMutability::Mutable,
2330 }),
2331 CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
2332 ],
2333 )
2334 .expect("Unable to generate authenticator_state_expire transaction!");
2335 builder
2336 }
2337
2338 fn setup_randomness_state_update<Mode: ExecutionMode>(
2339 update: RandomnessStateUpdate,
2340 temporary_store: &mut TemporaryStore<'_>,
2341 store: &dyn BackingStore,
2342 tx_ctx: Rc<RefCell<TxContext>>,
2343 move_vm: &Arc<MoveRuntime>,
2344 gas_charger: &mut GasCharger,
2345 protocol_config: &ProtocolConfig,
2346 metrics: Arc<ExecutionMetrics>,
2347 trace_builder_opt: &mut Option<MoveTraceBuilder>,
2348 ) -> Result<(), Mode::Error> {
2349 let pt = {
2350 let mut builder = ProgrammableTransactionBuilder::new();
2351 let res = builder.move_call(
2352 SUI_FRAMEWORK_ADDRESS.into(),
2353 RANDOMNESS_MODULE_NAME.to_owned(),
2354 RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
2355 vec![],
2356 vec![
2357 CallArg::Object(ObjectArg::SharedObject {
2358 id: SUI_RANDOMNESS_STATE_OBJECT_ID,
2359 initial_shared_version: update.randomness_obj_initial_shared_version,
2360 mutability: sui_types::transaction::SharedObjectMutability::Mutable,
2361 }),
2362 CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
2363 CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
2364 ],
2365 );
2366 assert_invariant!(
2367 res.is_ok(),
2368 "Unable to generate randomness_state_update transaction!"
2369 );
2370 builder.finish()
2371 };
2372 SPT::execute::<execution_mode::System<Mode::Error>>(
2373 protocol_config,
2374 metrics,
2375 move_vm,
2376 temporary_store,
2377 store,
2378 tx_ctx,
2379 gas_charger,
2380 None,
2381 pt,
2382 trace_builder_opt,
2383 )
2384 .map_err(|(e, _)| e)?;
2385 Ok(())
2386 }
2387
2388 fn setup_coin_deny_list_state_create(
2389 mut builder: ProgrammableTransactionBuilder,
2390 ) -> ProgrammableTransactionBuilder {
2391 builder
2392 .move_call(
2393 SUI_FRAMEWORK_ADDRESS.into(),
2394 DENY_LIST_MODULE.to_owned(),
2395 DENY_LIST_CREATE_FUNC.to_owned(),
2396 vec![],
2397 vec![],
2398 )
2399 .expect("Unable to generate coin_deny_list_create transaction!");
2400 builder
2401 }
2402
2403 fn setup_store_execution_time_estimates(
2404 mut builder: ProgrammableTransactionBuilder,
2405 estimates: StoredExecutionTimeObservations,
2406 chunk_size: usize,
2407 ) -> ProgrammableTransactionBuilder {
2408 let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
2409
2410 let estimate_chunks = estimates.chunk_observations(chunk_size);
2411
2412 let chunk_bytes: Vec<Vec<u8>> = estimate_chunks
2413 .into_iter()
2414 .map(|chunk| bcs::to_bytes(&chunk).unwrap())
2415 .collect();
2416
2417 let chunks_arg = builder.pure(chunk_bytes).unwrap();
2418
2419 builder.programmable_move_call(
2420 SUI_SYSTEM_PACKAGE_ID,
2421 SUI_SYSTEM_MODULE_NAME.to_owned(),
2422 ident_str!("store_execution_time_estimates_v2").to_owned(),
2423 vec![],
2424 vec![system_state, chunks_arg],
2425 );
2426 builder
2427 }
2428
2429 fn setup_accumulator_root_create(
2430 mut builder: ProgrammableTransactionBuilder,
2431 ) -> ProgrammableTransactionBuilder {
2432 builder
2433 .move_call(
2434 SUI_FRAMEWORK_ADDRESS.into(),
2435 ACCUMULATOR_ROOT_MODULE.to_owned(),
2436 ACCUMULATOR_ROOT_CREATE_FUNC.to_owned(),
2437 vec![],
2438 vec![],
2439 )
2440 .expect("Unable to generate accumulator_root_create transaction!");
2441 builder
2442 }
2443
2444 fn setup_write_accumulator_storage_cost(
2445 mut builder: ProgrammableTransactionBuilder,
2446 write_storage_cost: &WriteAccumulatorStorageCost,
2447 ) -> ProgrammableTransactionBuilder {
2448 let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
2449 let storage_cost_arg = builder.pure(write_storage_cost.storage_cost).unwrap();
2450 builder.programmable_move_call(
2451 SUI_SYSTEM_PACKAGE_ID,
2452 SUI_SYSTEM_MODULE_NAME.to_owned(),
2453 ident_str!("write_accumulator_storage_cost").to_owned(),
2454 vec![],
2455 vec![system_state, storage_cost_arg],
2456 );
2457 builder
2458 }
2459
2460 fn setup_coin_registry_create(
2461 mut builder: ProgrammableTransactionBuilder,
2462 ) -> ProgrammableTransactionBuilder {
2463 builder
2464 .move_call(
2465 SUI_FRAMEWORK_ADDRESS.into(),
2466 ident_str!("coin_registry").to_owned(),
2467 ident_str!("create").to_owned(),
2468 vec![],
2469 vec![],
2470 )
2471 .expect("Unable to generate coin_registry_create transaction!");
2472 builder
2473 }
2474
2475 fn setup_display_registry_create(
2476 mut builder: ProgrammableTransactionBuilder,
2477 ) -> ProgrammableTransactionBuilder {
2478 builder
2479 .move_call(
2480 SUI_FRAMEWORK_ADDRESS.into(),
2481 ident_str!("display_registry").to_owned(),
2482 ident_str!("create").to_owned(),
2483 vec![],
2484 vec![],
2485 )
2486 .expect("Unable to generate display_registry_create transaction!");
2487 builder
2488 }
2489
2490 fn setup_address_alias_state_create(
2491 mut builder: ProgrammableTransactionBuilder,
2492 ) -> ProgrammableTransactionBuilder {
2493 builder
2494 .move_call(
2495 SUI_FRAMEWORK_ADDRESS.into(),
2496 ident_str!("address_alias").to_owned(),
2497 ident_str!("create").to_owned(),
2498 vec![],
2499 vec![],
2500 )
2501 .expect("Unable to generate address_alias_state_create transaction!");
2502 builder
2503 }
2504 fn setup_forwarding_address_registry_create(
2505 mut builder: ProgrammableTransactionBuilder,
2506 ) -> ProgrammableTransactionBuilder {
2507 builder
2508 .move_call(
2509 SUI_FRAMEWORK_ADDRESS.into(),
2510 ident_str!("forwarding_address").to_owned(),
2511 ident_str!("create").to_owned(),
2512 vec![],
2513 vec![],
2514 )
2515 .expect("Unable to generate forwarding_address_registry_create transaction!");
2516 builder
2517 }
2518}