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