1pub use checked::*;
6
7#[iota_macros::with_checked_arithmetic]
8mod checked {
9
10 use std::{collections::HashSet, sync::Arc};
11
12 use iota_move_natives::all_natives;
13 use iota_protocol_config::{LimitThresholdCrossed, ProtocolConfig, check_limit_by_meter};
14 #[cfg(msim)]
15 use iota_types::iota_system_state::advance_epoch_result_injection::maybe_modify_result;
16 use iota_types::{
17 IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS, IOTA_FRAMEWORK_PACKAGE_ID,
18 IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID,
19 authenticator_state::{
20 AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME,
21 AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME, AUTHENTICATOR_STATE_MODULE_NAME,
22 AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
23 },
24 balance::{
25 BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
26 BALANCE_MODULE_NAME,
27 },
28 base_types::{
29 IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest, TxContext,
30 },
31 clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME},
32 committee::EpochId,
33 effects::TransactionEffects,
34 error::{ExecutionError, ExecutionErrorKind},
35 execution::{ExecutionResults, ExecutionResultsV1, is_certificate_denied},
36 execution_config_utils::to_binary_config,
37 execution_status::{CongestedObjects, ExecutionStatus},
38 gas::{GasCostSummary, IotaGasStatus},
39 gas_coin::GAS,
40 inner_temporary_store::InnerTemporaryStore,
41 iota_system_state::{
42 ADVANCE_EPOCH_FUNCTION_NAME, AdvanceEpochParams, IOTA_SYSTEM_MODULE_NAME,
43 },
44 messages_checkpoint::CheckpointTimestamp,
45 metrics::LimitsMetrics,
46 object::{OBJECT_START_VERSION, Object, ObjectInner},
47 programmable_transaction_builder::ProgrammableTransactionBuilder,
48 randomness_state::{RANDOMNESS_MODULE_NAME, RANDOMNESS_STATE_UPDATE_FUNCTION_NAME},
49 storage::{BackingStore, Storage},
50 transaction::{
51 Argument, AuthenticatorStateExpire, AuthenticatorStateUpdateV1, CallArg, ChangeEpoch,
52 ChangeEpochV2, CheckedInputObjects, Command, EndOfEpochTransactionKind,
53 GenesisTransaction, ObjectArg, ProgrammableTransaction, RandomnessStateUpdate,
54 TransactionKind,
55 },
56 };
57 use move_binary_format::CompiledModule;
58 use move_trace_format::format::MoveTraceBuilder;
59 use move_vm_runtime::move_vm::MoveVM;
60 use tracing::{info, instrument, trace, warn};
61
62 use crate::{
63 adapter::new_move_vm,
64 execution_mode::{self, ExecutionMode},
65 gas_charger::GasCharger,
66 programmable_transactions,
67 temporary_store::TemporaryStore,
68 type_layout_resolver::TypeLayoutResolver,
69 };
70
71 #[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
83 pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
84 store: &dyn BackingStore,
85 input_objects: CheckedInputObjects,
86 gas_coins: Vec<ObjectRef>,
87 gas_status: IotaGasStatus,
88 transaction_kind: TransactionKind,
89 transaction_signer: IotaAddress,
90 transaction_digest: TransactionDigest,
91 move_vm: &Arc<MoveVM>,
92 epoch_id: &EpochId,
93 epoch_timestamp_ms: u64,
94 protocol_config: &ProtocolConfig,
95 metrics: Arc<LimitsMetrics>,
96 enable_expensive_checks: bool,
97 certificate_deny_set: &HashSet<TransactionDigest>,
98 trace_builder_opt: &mut Option<MoveTraceBuilder>,
99 ) -> (
100 InnerTemporaryStore,
101 IotaGasStatus,
102 TransactionEffects,
103 Result<Mode::ExecutionResults, ExecutionError>,
104 ) {
105 let input_objects = input_objects.into_inner();
106 let mutable_inputs = if enable_expensive_checks {
107 input_objects.mutable_inputs().keys().copied().collect()
108 } else {
109 HashSet::new()
110 };
111 let shared_object_refs = input_objects.filter_shared_objects();
112 let receiving_objects = transaction_kind.receiving_objects();
113 let mut transaction_dependencies = input_objects.transaction_dependencies();
114 let contains_deleted_input = input_objects.contains_deleted_objects();
115 let cancelled_objects = input_objects.get_cancelled_objects();
116
117 let mut temporary_store = TemporaryStore::new(
118 store,
119 input_objects,
120 receiving_objects,
121 transaction_digest,
122 protocol_config,
123 *epoch_id,
124 );
125
126 let mut gas_charger =
127 GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
128
129 let mut tx_ctx = TxContext::new_from_components(
130 &transaction_signer,
131 &transaction_digest,
132 epoch_id,
133 epoch_timestamp_ms,
134 );
135
136 let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
137
138 let deny_cert = is_certificate_denied(&transaction_digest, certificate_deny_set);
139 let (gas_cost_summary, execution_result) = execute_transaction::<Mode>(
140 &mut temporary_store,
141 transaction_kind,
142 &mut gas_charger,
143 &mut tx_ctx,
144 move_vm,
145 protocol_config,
146 metrics,
147 enable_expensive_checks,
148 deny_cert,
149 contains_deleted_input,
150 cancelled_objects,
151 trace_builder_opt,
152 );
153
154 let status = if let Err(error) = &execution_result {
155 use ExecutionErrorKind as K;
157 match error.kind() {
158 K::InvariantViolation | K::VMInvariantViolation => {
159 #[skip_checked_arithmetic]
160 tracing::error!(
161 kind = ?error.kind(),
162 tx_digest = ?transaction_digest,
163 "INVARIANT VIOLATION! Source: {:?}",
164 error.source(),
165 );
166 }
167
168 K::IotaMoveVerificationError | K::VMVerificationOrDeserializationError => {
169 #[skip_checked_arithmetic]
170 tracing::debug!(
171 kind = ?error.kind(),
172 tx_digest = ?transaction_digest,
173 "Verification Error. Source: {:?}",
174 error.source(),
175 );
176 }
177
178 K::PublishUpgradeMissingDependency | K::PublishUpgradeDependencyDowngrade => {
179 #[skip_checked_arithmetic]
180 tracing::debug!(
181 kind = ?error.kind(),
182 tx_digest = ?transaction_digest,
183 "Publish/Upgrade Error. Source: {:?}",
184 error.source(),
185 )
186 }
187
188 _ => (),
189 };
190
191 let (status, command) = error.to_execution_status();
192 ExecutionStatus::new_failure(status, command)
193 } else {
194 ExecutionStatus::Success
195 };
196
197 #[skip_checked_arithmetic]
198 trace!(
199 tx_digest = ?transaction_digest,
200 computation_gas_cost = gas_cost_summary.computation_cost,
201 computation_gas_cost_burned = gas_cost_summary.computation_cost_burned,
202 storage_gas_cost = gas_cost_summary.storage_cost,
203 storage_gas_rebate = gas_cost_summary.storage_rebate,
204 "Finished execution of transaction with status {:?}",
205 status
206 );
207
208 transaction_dependencies.remove(&TransactionDigest::genesis_marker());
212
213 if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
214 temporary_store
215 .check_ownership_invariants(
216 &transaction_signer,
217 &mut gas_charger,
218 &mutable_inputs,
219 is_epoch_change,
220 )
221 .unwrap()
222 } let (inner, effects) = temporary_store.into_effects(
225 shared_object_refs,
226 &transaction_digest,
227 transaction_dependencies,
228 gas_cost_summary,
229 status,
230 &mut gas_charger,
231 *epoch_id,
232 );
233
234 (
235 inner,
236 gas_charger.into_gas_status(),
237 effects,
238 execution_result,
239 )
240 }
241
242 pub fn execute_genesis_state_update(
249 store: &dyn BackingStore,
250 protocol_config: &ProtocolConfig,
251 metrics: Arc<LimitsMetrics>,
252 move_vm: &Arc<MoveVM>,
253 tx_context: &mut TxContext,
254 input_objects: CheckedInputObjects,
255 pt: ProgrammableTransaction,
256 ) -> Result<InnerTemporaryStore, ExecutionError> {
257 let input_objects = input_objects.into_inner();
258 let mut temporary_store = TemporaryStore::new(
259 store,
260 input_objects,
261 vec![],
262 tx_context.digest(),
263 protocol_config,
264 0,
265 );
266 let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
267 programmable_transactions::execution::execute::<execution_mode::Genesis>(
268 protocol_config,
269 metrics,
270 move_vm,
271 &mut temporary_store,
272 tx_context,
273 &mut gas_charger,
274 pt,
275 &mut None,
276 )?;
277 temporary_store.update_object_version_and_prev_tx();
278 Ok(temporary_store.into_inner())
279 }
280
281 #[instrument(name = "tx_execute", level = "debug", skip_all)]
290 fn execute_transaction<Mode: ExecutionMode>(
291 temporary_store: &mut TemporaryStore<'_>,
292 transaction_kind: TransactionKind,
293 gas_charger: &mut GasCharger,
294 tx_ctx: &mut TxContext,
295 move_vm: &Arc<MoveVM>,
296 protocol_config: &ProtocolConfig,
297 metrics: Arc<LimitsMetrics>,
298 enable_expensive_checks: bool,
299 deny_cert: bool,
300 contains_deleted_input: bool,
301 cancelled_objects: Option<(Vec<ObjectID>, SequenceNumber)>,
302 trace_builder_opt: &mut Option<MoveTraceBuilder>,
303 ) -> (
304 GasCostSummary,
305 Result<Mode::ExecutionResults, ExecutionError>,
306 ) {
307 gas_charger.smash_gas(temporary_store);
308
309 debug_assert!(
311 gas_charger.no_charges(),
312 "No gas charges must be applied yet"
313 );
314
315 let is_genesis_or_epoch_change_tx = matches!(transaction_kind, TransactionKind::Genesis(_))
316 || transaction_kind.is_end_of_epoch_tx();
317
318 let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
319
320 let result = gas_charger.charge_input_objects(temporary_store);
324 let mut result = result.and_then(|()| {
325 let mut execution_result = if deny_cert {
326 Err(ExecutionError::new(
327 ExecutionErrorKind::CertificateDenied,
328 None,
329 ))
330 } else if contains_deleted_input {
331 Err(ExecutionError::new(
332 ExecutionErrorKind::InputObjectDeleted,
333 None,
334 ))
335 } else if let Some((cancelled_objects, reason)) = cancelled_objects {
336 match reason {
337 version if version.is_congested() => Err(ExecutionError::new(
338 if protocol_config.congestion_control_gas_price_feedback_mechanism() {
339 ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestionV2 {
340 congested_objects: CongestedObjects(cancelled_objects),
341 suggested_gas_price: version
342 .get_congested_version_suggested_gas_price(),
343 }
344 } else {
345 ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
350 congested_objects: CongestedObjects(cancelled_objects),
351 }
352 },
353 None,
354 )),
355 SequenceNumber::RANDOMNESS_UNAVAILABLE => Err(ExecutionError::new(
356 ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable,
357 None,
358 )),
359 _ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
360 }
361 } else {
362 execution_loop::<Mode>(
363 temporary_store,
364 transaction_kind,
365 tx_ctx,
366 move_vm,
367 gas_charger,
368 protocol_config,
369 metrics.clone(),
370 trace_builder_opt,
371 )
372 };
373
374 let meter_check = check_meter_limit(
375 temporary_store,
376 gas_charger,
377 protocol_config,
378 metrics.clone(),
379 );
380 if let Err(e) = meter_check {
381 execution_result = Err(e);
382 }
383
384 if execution_result.is_ok() {
385 let gas_check = check_written_objects_limit::<Mode>(
386 temporary_store,
387 gas_charger,
388 protocol_config,
389 metrics,
390 );
391 if let Err(e) = gas_check {
392 execution_result = Err(e);
393 }
394 }
395
396 execution_result
397 });
398
399 let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
400 temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
409
410 if let Err(e) = run_conservation_checks::<Mode>(
411 temporary_store,
412 gas_charger,
413 tx_ctx,
414 move_vm,
415 enable_expensive_checks,
416 &cost_summary,
417 is_genesis_or_epoch_change_tx,
418 advance_epoch_gas_summary,
419 ) {
420 result = Err(e);
422 }
423
424 (cost_summary, result)
425 }
426
427 #[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
436 fn run_conservation_checks<Mode: ExecutionMode>(
437 temporary_store: &mut TemporaryStore<'_>,
438 gas_charger: &mut GasCharger,
439 tx_ctx: &mut TxContext,
440 move_vm: &Arc<MoveVM>,
441 enable_expensive_checks: bool,
442 cost_summary: &GasCostSummary,
443 is_genesis_or_epoch_change_tx: bool,
444 advance_epoch_gas_summary: Option<(u64, u64)>,
445 ) -> Result<(), ExecutionError> {
446 let mut result: std::result::Result<(), iota_types::error::ExecutionError> = Ok(());
447 if !is_genesis_or_epoch_change_tx && !Mode::skip_conservation_checks() {
448 let conservation_result = {
451 temporary_store
452 .check_iota_conserved(cost_summary)
453 .and_then(|()| {
454 if enable_expensive_checks {
455 let mut layout_resolver =
458 TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
459 temporary_store.check_iota_conserved_expensive(
460 cost_summary,
461 advance_epoch_gas_summary,
462 &mut layout_resolver,
463 )
464 } else {
465 Ok(())
466 }
467 })
468 };
469 if let Err(conservation_err) = conservation_result {
470 result = Err(conservation_err);
474 gas_charger.reset(temporary_store);
475 gas_charger.charge_gas(temporary_store, &mut result);
476 if let Err(recovery_err) = {
478 temporary_store
479 .check_iota_conserved(cost_summary)
480 .and_then(|()| {
481 if enable_expensive_checks {
482 let mut layout_resolver =
485 TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
486 temporary_store.check_iota_conserved_expensive(
487 cost_summary,
488 advance_epoch_gas_summary,
489 &mut layout_resolver,
490 )
491 } else {
492 Ok(())
493 }
494 })
495 } {
496 panic!(
500 "IOTA conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
501 tx_ctx.digest(),
502 recovery_err,
503 gas_charger.summary()
504 )
505 }
506 }
507 } result
511 }
512
513 #[instrument(name = "check_meter_limit", level = "debug", skip_all)]
518 fn check_meter_limit(
519 temporary_store: &mut TemporaryStore<'_>,
520 gas_charger: &mut GasCharger,
521 protocol_config: &ProtocolConfig,
522 metrics: Arc<LimitsMetrics>,
523 ) -> Result<(), ExecutionError> {
524 let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
525
526 match check_limit_by_meter!(
531 !gas_charger.is_unmetered(),
532 effects_estimated_size,
533 protocol_config.max_serialized_tx_effects_size_bytes(),
534 protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
535 metrics.excessive_estimated_effects_size
536 ) {
537 LimitThresholdCrossed::None => Ok(()),
538 LimitThresholdCrossed::Soft(_, limit) => {
539 warn!(
540 effects_estimated_size = effects_estimated_size,
541 soft_limit = limit,
542 "Estimated transaction effects size crossed soft limit",
543 );
544 Ok(())
545 }
546 LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
547 ExecutionErrorKind::EffectsTooLarge {
548 current_size: effects_estimated_size as u64,
549 max_size: lim as u64,
550 },
551 "Transaction effects are too large",
552 )),
553 }
554 }
555
556 #[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
561 fn check_written_objects_limit<Mode: ExecutionMode>(
562 temporary_store: &mut TemporaryStore<'_>,
563 gas_charger: &mut GasCharger,
564 protocol_config: &ProtocolConfig,
565 metrics: Arc<LimitsMetrics>,
566 ) -> Result<(), ExecutionError> {
567 if let (Some(normal_lim), Some(system_lim)) = (
568 protocol_config.max_size_written_objects_as_option(),
569 protocol_config.max_size_written_objects_system_tx_as_option(),
570 ) {
571 let written_objects_size = temporary_store.written_objects_size();
572
573 match check_limit_by_meter!(
574 !gas_charger.is_unmetered(),
575 written_objects_size,
576 normal_lim,
577 system_lim,
578 metrics.excessive_written_objects_size
579 ) {
580 LimitThresholdCrossed::None => (),
581 LimitThresholdCrossed::Soft(_, limit) => {
582 warn!(
583 written_objects_size = written_objects_size,
584 soft_limit = limit,
585 "Written objects size crossed soft limit",
586 )
587 }
588 LimitThresholdCrossed::Hard(_, lim) => {
589 return Err(ExecutionError::new_with_source(
590 ExecutionErrorKind::WrittenObjectsTooLarge {
591 current_size: written_objects_size as u64,
592 max_size: lim as u64,
593 },
594 "Written objects size crossed hard limit",
595 ));
596 }
597 };
598 }
599
600 Ok(())
601 }
602
603 #[instrument(level = "debug", skip_all)]
610 fn execution_loop<Mode: ExecutionMode>(
611 temporary_store: &mut TemporaryStore<'_>,
612 transaction_kind: TransactionKind,
613 tx_ctx: &mut TxContext,
614 move_vm: &Arc<MoveVM>,
615 gas_charger: &mut GasCharger,
616 protocol_config: &ProtocolConfig,
617 metrics: Arc<LimitsMetrics>,
618 trace_builder_opt: &mut Option<MoveTraceBuilder>,
619 ) -> Result<Mode::ExecutionResults, ExecutionError> {
620 let result = match transaction_kind {
621 TransactionKind::Genesis(GenesisTransaction { objects, events }) => {
622 if tx_ctx.epoch() != 0 {
623 panic!("BUG: Genesis Transactions can only be executed in epoch 0");
624 }
625
626 for genesis_object in objects {
627 match genesis_object {
628 iota_types::transaction::GenesisObject::RawObject { data, owner } => {
629 let object = ObjectInner {
630 data,
631 owner,
632 previous_transaction: tx_ctx.digest(),
633 storage_rebate: 0,
634 };
635 temporary_store.create_object(object.into());
636 }
637 }
638 }
639
640 temporary_store.record_execution_results(ExecutionResults::V1(
641 ExecutionResultsV1 {
642 user_events: events,
643 ..Default::default()
644 },
645 ));
646
647 Ok(Mode::empty_results())
648 }
649 TransactionKind::ConsensusCommitPrologueV1(prologue) => {
650 setup_consensus_commit(
651 prologue.commit_timestamp_ms,
652 temporary_store,
653 tx_ctx,
654 move_vm,
655 gas_charger,
656 protocol_config,
657 metrics,
658 trace_builder_opt,
659 )
660 .expect("ConsensusCommitPrologueV1 cannot fail");
661 Ok(Mode::empty_results())
662 }
663 TransactionKind::ProgrammableTransaction(pt) => {
664 programmable_transactions::execution::execute::<Mode>(
665 protocol_config,
666 metrics,
667 move_vm,
668 temporary_store,
669 tx_ctx,
670 gas_charger,
671 pt,
672 trace_builder_opt,
673 )
674 }
675 TransactionKind::EndOfEpochTransaction(txns) => {
676 let mut builder = ProgrammableTransactionBuilder::new();
677 let len = txns.len();
678 for (i, tx) in txns.into_iter().enumerate() {
679 match tx {
680 EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
681 assert_eq!(i, len - 1);
682 advance_epoch_v1(
683 builder,
684 change_epoch,
685 temporary_store,
686 tx_ctx,
687 move_vm,
688 gas_charger,
689 protocol_config,
690 metrics,
691 trace_builder_opt,
692 )?;
693 return Ok(Mode::empty_results());
694 }
695 EndOfEpochTransactionKind::ChangeEpochV2(change_epoch_v2) => {
696 assert_eq!(i, len - 1);
697 advance_epoch_v2(
698 builder,
699 change_epoch_v2,
700 temporary_store,
701 tx_ctx,
702 move_vm,
703 gas_charger,
704 protocol_config,
705 metrics,
706 trace_builder_opt,
707 )?;
708 return Ok(Mode::empty_results());
709 }
710 EndOfEpochTransactionKind::AuthenticatorStateCreate => {
711 assert!(protocol_config.enable_jwk_consensus_updates());
712 builder = setup_authenticator_state_create(builder);
713 }
714 EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
715 assert!(protocol_config.enable_jwk_consensus_updates());
716
717 builder = setup_authenticator_state_expire(builder, expire);
720 }
721 }
722 }
723 unreachable!(
724 "EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list"
725 )
726 }
727 TransactionKind::AuthenticatorStateUpdateV1(auth_state_update) => {
728 setup_authenticator_state_update(
729 auth_state_update,
730 temporary_store,
731 tx_ctx,
732 move_vm,
733 gas_charger,
734 protocol_config,
735 metrics,
736 trace_builder_opt,
737 )?;
738 Ok(Mode::empty_results())
739 }
740 TransactionKind::RandomnessStateUpdate(randomness_state_update) => {
741 setup_randomness_state_update(
742 randomness_state_update,
743 temporary_store,
744 tx_ctx,
745 move_vm,
746 gas_charger,
747 protocol_config,
748 metrics,
749 trace_builder_opt,
750 )?;
751 Ok(Mode::empty_results())
752 }
753 }?;
754 temporary_store.check_execution_results_consistency()?;
755 Ok(result)
756 }
757
758 fn mint_epoch_rewards_in_pt(
767 builder: &mut ProgrammableTransactionBuilder,
768 params: &AdvanceEpochParams,
769 ) -> (Argument, Argument) {
770 let storage_charge_arg = builder
772 .input(CallArg::Pure(
773 bcs::to_bytes(¶ms.storage_charge).unwrap(),
774 ))
775 .unwrap();
776 let storage_charges = builder.programmable_move_call(
777 IOTA_FRAMEWORK_PACKAGE_ID,
778 BALANCE_MODULE_NAME.to_owned(),
779 BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
780 vec![GAS::type_tag()],
781 vec![storage_charge_arg],
782 );
783
784 let computation_charge_arg = builder
786 .input(CallArg::Pure(
787 bcs::to_bytes(¶ms.computation_charge).unwrap(),
788 ))
789 .unwrap();
790 let computation_charges = builder.programmable_move_call(
791 IOTA_FRAMEWORK_PACKAGE_ID,
792 BALANCE_MODULE_NAME.to_owned(),
793 BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
794 vec![GAS::type_tag()],
795 vec![computation_charge_arg],
796 );
797 (storage_charges, computation_charges)
798 }
799
800 pub fn construct_advance_epoch_pt_impl(
808 mut builder: ProgrammableTransactionBuilder,
809 params: &AdvanceEpochParams,
810 call_arg_vec: Vec<CallArg>,
811 ) -> Result<ProgrammableTransaction, ExecutionError> {
812 let (storage_charges, computation_charges) = mint_epoch_rewards_in_pt(&mut builder, params);
814 let mut arguments = vec![
815 builder
816 .pure(params.validator_subsidy)
817 .expect("bcs encoding a u64 should not fail"),
818 storage_charges,
819 computation_charges,
820 ];
821
822 let call_arg_arguments = call_arg_vec
823 .into_iter()
824 .map(|a| builder.input(a))
825 .collect::<Result<_, _>>();
826
827 assert_invariant!(
828 call_arg_arguments.is_ok(),
829 "Unable to generate args for advance_epoch transaction!"
830 );
831
832 arguments.append(&mut call_arg_arguments.unwrap());
833
834 info!("Call arguments to advance_epoch transaction: {:?}", params);
835
836 let storage_rebates = builder.programmable_move_call(
837 IOTA_SYSTEM_PACKAGE_ID,
838 IOTA_SYSTEM_MODULE_NAME.to_owned(),
839 ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
840 vec![],
841 arguments,
842 );
843
844 builder.programmable_move_call(
846 IOTA_FRAMEWORK_PACKAGE_ID,
847 BALANCE_MODULE_NAME.to_owned(),
848 BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
849 vec![GAS::type_tag()],
850 vec![storage_rebates],
851 );
852 Ok(builder.finish())
853 }
854
855 pub fn construct_advance_epoch_pt_v1(
856 builder: ProgrammableTransactionBuilder,
857 params: &AdvanceEpochParams,
858 ) -> Result<ProgrammableTransaction, ExecutionError> {
859 let call_arg_vec = vec![
864 CallArg::IOTA_SYSTEM_MUT, CallArg::Pure(bcs::to_bytes(¶ms.epoch).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.next_protocol_version.as_u64()).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.storage_rebate).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.non_refundable_storage_fee).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.reward_slashing_rate).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.epoch_start_timestamp_ms).unwrap()), ];
872 construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
873 }
874
875 pub fn construct_advance_epoch_pt_v2(
876 builder: ProgrammableTransactionBuilder,
877 params: &AdvanceEpochParams,
878 ) -> Result<ProgrammableTransaction, ExecutionError> {
879 let call_arg_vec = vec![
884 CallArg::Pure(bcs::to_bytes(¶ms.computation_charge_burned).unwrap()), CallArg::IOTA_SYSTEM_MUT, CallArg::Pure(bcs::to_bytes(¶ms.epoch).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.next_protocol_version.as_u64()).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.storage_rebate).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.non_refundable_storage_fee).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.reward_slashing_rate).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.epoch_start_timestamp_ms).unwrap()), CallArg::Pure(bcs::to_bytes(¶ms.max_committee_members_count).unwrap()), ];
894 construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
895 }
896
897 fn advance_epoch_impl(
904 advance_epoch_pt: ProgrammableTransaction,
905 params: AdvanceEpochParams,
906 system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
907 temporary_store: &mut TemporaryStore<'_>,
908 tx_ctx: &mut TxContext,
909 move_vm: &Arc<MoveVM>,
910 gas_charger: &mut GasCharger,
911 protocol_config: &ProtocolConfig,
912 metrics: Arc<LimitsMetrics>,
913 trace_builder_opt: &mut Option<MoveTraceBuilder>,
914 ) -> Result<(), ExecutionError> {
915 let result = programmable_transactions::execution::execute::<execution_mode::System>(
916 protocol_config,
917 metrics.clone(),
918 move_vm,
919 temporary_store,
920 tx_ctx,
921 gas_charger,
922 advance_epoch_pt,
923 trace_builder_opt,
924 );
925
926 #[cfg(msim)]
927 let result = maybe_modify_result(result, params.epoch);
928
929 if result.is_err() {
930 tracing::error!(
931 "Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx params: {:?}",
932 result.as_ref().err(),
933 temporary_store.objects(),
934 params,
935 );
936 temporary_store.drop_writes();
937 gas_charger.reset_storage_cost_and_rebate();
939
940 temporary_store.advance_epoch_safe_mode(¶ms, protocol_config);
941 }
942
943 let new_vm = new_move_vm(
944 all_natives(true, protocol_config),
945 protocol_config,
946 None,
948 )
949 .expect("Failed to create new MoveVM");
950 process_system_packages(
951 system_packages,
952 temporary_store,
953 tx_ctx,
954 &new_vm,
955 gas_charger,
956 protocol_config,
957 metrics,
958 trace_builder_opt,
959 );
960
961 Ok(())
962 }
963
964 fn advance_epoch_v1(
968 builder: ProgrammableTransactionBuilder,
969 change_epoch: ChangeEpoch,
970 temporary_store: &mut TemporaryStore<'_>,
971 tx_ctx: &mut TxContext,
972 move_vm: &Arc<MoveVM>,
973 gas_charger: &mut GasCharger,
974 protocol_config: &ProtocolConfig,
975 metrics: Arc<LimitsMetrics>,
976 trace_builder_opt: &mut Option<MoveTraceBuilder>,
977 ) -> Result<(), ExecutionError> {
978 let params = AdvanceEpochParams {
979 epoch: change_epoch.epoch,
980 next_protocol_version: change_epoch.protocol_version,
981 validator_subsidy: protocol_config.validator_target_reward(),
982 storage_charge: change_epoch.storage_charge,
983 computation_charge: change_epoch.computation_charge,
984 computation_charge_burned: change_epoch.computation_charge,
986 storage_rebate: change_epoch.storage_rebate,
987 non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
988 reward_slashing_rate: protocol_config.reward_slashing_rate(),
989 epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
990 max_committee_members_count: 0,
993 };
994 let advance_epoch_pt = construct_advance_epoch_pt_v1(builder, ¶ms)?;
995 advance_epoch_impl(
996 advance_epoch_pt,
997 params,
998 change_epoch.system_packages,
999 temporary_store,
1000 tx_ctx,
1001 move_vm,
1002 gas_charger,
1003 protocol_config,
1004 metrics,
1005 trace_builder_opt,
1006 )
1007 }
1008
1009 fn advance_epoch_v2(
1013 builder: ProgrammableTransactionBuilder,
1014 change_epoch_v2: ChangeEpochV2,
1015 temporary_store: &mut TemporaryStore<'_>,
1016 tx_ctx: &mut TxContext,
1017 move_vm: &Arc<MoveVM>,
1018 gas_charger: &mut GasCharger,
1019 protocol_config: &ProtocolConfig,
1020 metrics: Arc<LimitsMetrics>,
1021 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1022 ) -> Result<(), ExecutionError> {
1023 let params = AdvanceEpochParams {
1024 epoch: change_epoch_v2.epoch,
1025 next_protocol_version: change_epoch_v2.protocol_version,
1026 validator_subsidy: protocol_config.validator_target_reward(),
1027 storage_charge: change_epoch_v2.storage_charge,
1028 computation_charge: change_epoch_v2.computation_charge,
1029 computation_charge_burned: change_epoch_v2.computation_charge_burned,
1030 storage_rebate: change_epoch_v2.storage_rebate,
1031 non_refundable_storage_fee: change_epoch_v2.non_refundable_storage_fee,
1032 reward_slashing_rate: protocol_config.reward_slashing_rate(),
1033 epoch_start_timestamp_ms: change_epoch_v2.epoch_start_timestamp_ms,
1034 max_committee_members_count: protocol_config.max_committee_members_count(),
1035 };
1036 let advance_epoch_pt = construct_advance_epoch_pt_v2(builder, ¶ms)?;
1037 advance_epoch_impl(
1038 advance_epoch_pt,
1039 params,
1040 change_epoch_v2.system_packages,
1041 temporary_store,
1042 tx_ctx,
1043 move_vm,
1044 gas_charger,
1045 protocol_config,
1046 metrics,
1047 trace_builder_opt,
1048 )
1049 }
1050
1051 fn process_system_packages(
1052 system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
1053 temporary_store: &mut TemporaryStore<'_>,
1054 tx_ctx: &mut TxContext,
1055 move_vm: &MoveVM,
1056 gas_charger: &mut GasCharger,
1057 protocol_config: &ProtocolConfig,
1058 metrics: Arc<LimitsMetrics>,
1059 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1060 ) {
1061 let binary_config = to_binary_config(protocol_config);
1062 for (version, modules, dependencies) in system_packages.into_iter() {
1063 let deserialized_modules: Vec<_> = modules
1064 .iter()
1065 .map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
1066 .collect();
1067
1068 if version == OBJECT_START_VERSION {
1069 let package_id = deserialized_modules.first().unwrap().address();
1070 info!("adding new system package {package_id}");
1071
1072 let publish_pt = {
1073 let mut b = ProgrammableTransactionBuilder::new();
1074 b.command(Command::Publish(modules, dependencies));
1075 b.finish()
1076 };
1077
1078 programmable_transactions::execution::execute::<execution_mode::System>(
1079 protocol_config,
1080 metrics.clone(),
1081 move_vm,
1082 temporary_store,
1083 tx_ctx,
1084 gas_charger,
1085 publish_pt,
1086 trace_builder_opt,
1087 )
1088 .expect("System Package Publish must succeed");
1089 } else {
1090 let mut new_package = Object::new_system_package(
1091 &deserialized_modules,
1092 version,
1093 dependencies,
1094 tx_ctx.digest(),
1095 );
1096
1097 info!(
1098 "upgraded system package {:?}",
1099 new_package.compute_object_reference()
1100 );
1101
1102 new_package
1105 .data
1106 .try_as_package_mut()
1107 .unwrap()
1108 .decrement_version();
1109
1110 temporary_store.upgrade_system_package(new_package);
1112 }
1113 }
1114 }
1115
1116 fn setup_consensus_commit(
1122 consensus_commit_timestamp_ms: CheckpointTimestamp,
1123 temporary_store: &mut TemporaryStore<'_>,
1124 tx_ctx: &mut TxContext,
1125 move_vm: &Arc<MoveVM>,
1126 gas_charger: &mut GasCharger,
1127 protocol_config: &ProtocolConfig,
1128 metrics: Arc<LimitsMetrics>,
1129 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1130 ) -> Result<(), ExecutionError> {
1131 let pt = {
1132 let mut builder = ProgrammableTransactionBuilder::new();
1133 let res = builder.move_call(
1134 IOTA_FRAMEWORK_ADDRESS.into(),
1135 CLOCK_MODULE_NAME.to_owned(),
1136 CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
1137 vec![],
1138 vec![
1139 CallArg::CLOCK_MUT,
1140 CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
1141 ],
1142 );
1143 assert_invariant!(
1144 res.is_ok(),
1145 "Unable to generate consensus_commit_prologue transaction!"
1146 );
1147 builder.finish()
1148 };
1149 programmable_transactions::execution::execute::<execution_mode::System>(
1150 protocol_config,
1151 metrics,
1152 move_vm,
1153 temporary_store,
1154 tx_ctx,
1155 gas_charger,
1156 pt,
1157 trace_builder_opt,
1158 )
1159 }
1160
1161 fn setup_authenticator_state_create(
1165 mut builder: ProgrammableTransactionBuilder,
1166 ) -> ProgrammableTransactionBuilder {
1167 builder
1168 .move_call(
1169 IOTA_FRAMEWORK_ADDRESS.into(),
1170 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1171 AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
1172 vec![],
1173 vec![],
1174 )
1175 .expect("Unable to generate authenticator_state_create transaction!");
1176 builder
1177 }
1178
1179 fn setup_authenticator_state_update(
1186 update: AuthenticatorStateUpdateV1,
1187 temporary_store: &mut TemporaryStore<'_>,
1188 tx_ctx: &mut TxContext,
1189 move_vm: &Arc<MoveVM>,
1190 gas_charger: &mut GasCharger,
1191 protocol_config: &ProtocolConfig,
1192 metrics: Arc<LimitsMetrics>,
1193 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1194 ) -> Result<(), ExecutionError> {
1195 let pt = {
1196 let mut builder = ProgrammableTransactionBuilder::new();
1197 let res = builder.move_call(
1198 IOTA_FRAMEWORK_ADDRESS.into(),
1199 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1200 AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1201 vec![],
1202 vec![
1203 CallArg::Object(ObjectArg::SharedObject {
1204 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1205 initial_shared_version: update.authenticator_obj_initial_shared_version,
1206 mutable: true,
1207 }),
1208 CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
1209 ],
1210 );
1211 assert_invariant!(
1212 res.is_ok(),
1213 "Unable to generate authenticator_state_update transaction!"
1214 );
1215 builder.finish()
1216 };
1217 programmable_transactions::execution::execute::<execution_mode::System>(
1218 protocol_config,
1219 metrics,
1220 move_vm,
1221 temporary_store,
1222 tx_ctx,
1223 gas_charger,
1224 pt,
1225 trace_builder_opt,
1226 )
1227 }
1228
1229 fn setup_authenticator_state_expire(
1234 mut builder: ProgrammableTransactionBuilder,
1235 expire: AuthenticatorStateExpire,
1236 ) -> ProgrammableTransactionBuilder {
1237 builder
1238 .move_call(
1239 IOTA_FRAMEWORK_ADDRESS.into(),
1240 AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
1241 AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
1242 vec![],
1243 vec![
1244 CallArg::Object(ObjectArg::SharedObject {
1245 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
1246 initial_shared_version: expire.authenticator_obj_initial_shared_version,
1247 mutable: true,
1248 }),
1249 CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
1250 ],
1251 )
1252 .expect("Unable to generate authenticator_state_expire transaction!");
1253 builder
1254 }
1255
1256 fn setup_randomness_state_update(
1262 update: RandomnessStateUpdate,
1263 temporary_store: &mut TemporaryStore<'_>,
1264 tx_ctx: &mut TxContext,
1265 move_vm: &Arc<MoveVM>,
1266 gas_charger: &mut GasCharger,
1267 protocol_config: &ProtocolConfig,
1268 metrics: Arc<LimitsMetrics>,
1269 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1270 ) -> Result<(), ExecutionError> {
1271 let pt = {
1272 let mut builder = ProgrammableTransactionBuilder::new();
1273 let res = builder.move_call(
1274 IOTA_FRAMEWORK_ADDRESS.into(),
1275 RANDOMNESS_MODULE_NAME.to_owned(),
1276 RANDOMNESS_STATE_UPDATE_FUNCTION_NAME.to_owned(),
1277 vec![],
1278 vec![
1279 CallArg::Object(ObjectArg::SharedObject {
1280 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
1281 initial_shared_version: update.randomness_obj_initial_shared_version,
1282 mutable: true,
1283 }),
1284 CallArg::Pure(bcs::to_bytes(&update.randomness_round).unwrap()),
1285 CallArg::Pure(bcs::to_bytes(&update.random_bytes).unwrap()),
1286 ],
1287 );
1288 assert_invariant!(
1289 res.is_ok(),
1290 "Unable to generate randomness_state_update transaction!"
1291 );
1292 builder.finish()
1293 };
1294 programmable_transactions::execution::execute::<execution_mode::System>(
1295 protocol_config,
1296 metrics,
1297 move_vm,
1298 temporary_store,
1299 tx_ctx,
1300 gas_charger,
1301 pt,
1302 trace_builder_opt,
1303 )
1304 }
1305}