1use crate::{
5 adapter,
6 execution_mode::ExecutionMode,
7 execution_value::ExecutionState,
8 gas_charger::{GasCharger, GasPayment, PaymentLocation},
9 gas_meter::SuiGasMeter,
10 sp,
11 static_programmable_transactions::{
12 env::Env,
13 execution::{
14 self, trace_utils,
15 values::{Local, Locals, Value},
16 },
17 linkage::resolved_linkage::{ExecutableLinkage, ResolvedLinkage},
18 loading::ast::{Datatype, DeserializedPackage, PackagePayload},
19 typing::ast::{self as T, Type},
20 },
21};
22use indexmap::{IndexMap, IndexSet};
23use move_binary_format::{
24 CompiledModule,
25 compatibility::{Compatibility, InclusionCheck},
26 errors::{Location, PartialVMError, PartialVMResult, VMResult},
27 file_format::FunctionDefinitionIndex,
28 normalized,
29};
30use move_core_types::{
31 account_address::AccountAddress,
32 identifier::IdentStr,
33 language_storage::{ModuleId, StructTag},
34 u256::U256,
35};
36use move_trace_format::format::MoveTraceBuilder;
37use move_vm_runtime::{
38 execution::{
39 Type as VMType, TypeSubst as _,
40 values::{VMValueCast, Value as VMValue},
41 vm::{LoadedFunctionInformation, MoveVM},
42 },
43 natives::extensions::NativeExtensions,
44 shared::{
45 gas::{GasMeter as _, SimpleInstruction},
46 linkage_context::LinkageHash,
47 },
48 validation::verification::ast::Package as VerifiedPackage,
49};
50use mysten_common::ZipDebugEqIteratorExt;
51use mysten_common::debug_fatal;
52use nonempty::nonempty;
53use quick_cache::unsync::Cache as QCache;
54use serde::{Deserialize, de::DeserializeSeed};
55use std::{
56 cell::RefCell,
57 collections::{BTreeMap, BTreeSet},
58 fmt,
59 rc::Rc,
60 sync::Arc,
61};
62use sui_move_natives::object_runtime::{
63 self, LoadedRuntimeObject, MoveAccumulatorAction, MoveAccumulatorEvent, MoveAccumulatorValue,
64 ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
65};
66use sui_protocol_config::ProtocolConfig;
67use sui_types::{
68 Identifier, TypeTag,
69 accumulator_event::AccumulatorEvent,
70 accumulator_root::{
71 self, AccumulatorObjId, SETTLEMENT_MAX_TYPE_INSTANTIATION_NODES, is_settle_u128_call,
72 },
73 balance::Balance,
74 base_types::{
75 MoveObjectType, ObjectID, RESOLVED_ASCII_STR, RESOLVED_UTF8_STR, SequenceNumber,
76 SuiAddress, TxContext,
77 },
78 effects::{AccumulatorAddress, AccumulatorValue, AccumulatorWriteV1},
79 error::{ExecutionError, ExecutionErrorTrait, SafeIndex, command_argument_error},
80 event::Event,
81 execution::{ExecutionResults, ExecutionResultsV2},
82 execution_status::{CommandArgumentError, ExecutionErrorKind, PackageUpgradeError},
83 metrics::ExecutionMetrics,
84 move_package::{
85 MovePackage, UpgradeCap, UpgradePolicy, UpgradeReceipt, UpgradeTicket,
86 normalize_deserialized_modules,
87 },
88 object::{MoveObject, Object, ObjectPermissions, Owner},
89 storage::{BackingPackageStore, DenyListResult, PackageObject, get_package_objects},
90};
91use sui_verifier::INIT_FN_NAME;
92use tracing::instrument;
93
94const PUBLISH_INIT_EXPECTED_STACK_HEIGHT: u64 = 0;
96const UPGRADE_INIT_EXPECTED_STACK_HEIGHT: u64 = 1;
99
100macro_rules! unwrap {
101 ($e:expr, $($args:expr),* $(,)?) => {
102 match $e {
103 Some(v) => v,
104 None => {
105 invariant_violation!("Unexpected none: {}", format!($($args),*))
106 }
107 }
108
109 };
110}
111
112#[macro_export]
113macro_rules! object_runtime {
114 ($context:ident) => {
115 $context
116 .native_extensions
117 .try_borrow()
118 .map_err(|_| {
119 make_invariant_violation!(
120 "Should be able to borrow object runtime native extension"
121 )
122 })?
123 .get::<sui_move_natives::object_runtime::ObjectRuntime>()
124 .map_err(|e| {
125 $context
126 .env
127 .convert_vm_error(e.finish(move_binary_format::errors::Location::Undefined))
128 })
129 };
130}
131
132macro_rules! object_runtime_mut {
133 ($context:ident) => {
134 $context
135 .native_extensions
136 .try_borrow_mut()
137 .map_err(|_| {
138 make_invariant_violation!(
139 "Should be able to borrow object runtime native extension"
140 )
141 })?
142 .get_mut::<ObjectRuntime>()
143 .map_err(|e| $context.env.convert_vm_error(e.finish(Location::Undefined)))
144 };
145}
146
147macro_rules! charge_gas_ {
148 ($gas_charger:expr, $env:expr, $call:ident($($args:expr),*)) => {{
149 SuiGasMeter($gas_charger.move_gas_status_mut())
150 .$call($($args),*)
151 .map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
152 }};
153 ($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {
154 charge_gas_!($gas_charger, $env, $case($value_view))
155 };
156}
157
158macro_rules! charge_gas {
159 ($context:ident, $case:ident, $value_view:expr) => {{ charge_gas_!($context.gas_charger, $context.env, $case, $value_view) }};
160}
161
162macro_rules! with_vm {
165 ($self:ident, $linkage:expr, $error:ty, $body:expr) => {{
166 let link_context = $linkage.linkage_context::<$error>()?;
167 let linkage_hash = link_context.to_linkage_hash();
168 let mut vm = if let Some((_, vm)) = $self.executable_vm_cache.remove(&linkage_hash) {
169 vm
170 } else {
171 let data_store = &$self.env.linkable_store.package_store;
172 $self
173 .env
174 .vm
175 .make_vm_with_native_extensions(
176 data_store,
177 link_context.clone(),
178 $self.native_extensions.clone(),
179 )
180 .map_err(|e| $self.env.convert_linked_vm_error(e, $linkage))?
181 };
182 let result = $body(&mut vm)?;
183 $self.executable_vm_cache.insert(linkage_hash, vm);
184 Ok(result)
185 }};
186}
187
188#[derive(Debug)]
190pub struct CtxValue(Value);
191
192#[derive(Clone, Debug)]
193pub struct InputObjectMetadata {
194 pub newly_created: bool,
195 pub id: ObjectID,
196 pub refined_permissions: ObjectPermissions,
197 pub owner: Owner,
198 pub version: SequenceNumber,
199 pub type_: Type,
200}
201
202#[derive(Debug, Clone, Copy)]
206pub(crate) enum GasCoinTransfer {
207 TransferObjects,
209 SendFunds {
211 recipient: AccountAddress,
213 },
214}
215
216#[derive(Copy, Clone)]
217enum UsageKind {
218 Move,
219 Copy,
220 Borrow,
221}
222
223struct Locations {
225 tx_context_value: Locals,
227 gas: Option<(GasPayment, InputObjectMetadata, Locals)>,
229 input_object_metadata: Vec<(T::InputIndex, InputObjectMetadata)>,
231 object_inputs: Locals,
232 input_withdrawal_metadata: Vec<T::WithdrawalInput>,
233 withdrawal_inputs: Locals,
234 pure_input_bytes: IndexSet<Vec<u8>>,
235 pure_input_metadata: Vec<T::PureInput>,
236 pure_inputs: Locals,
237 receiving_input_metadata: Vec<T::ReceivingInput>,
238 receiving_inputs: Locals,
239 results: Vec<Locals>,
243}
244
245enum ResolvedLocation<'a> {
246 Local(Local<'a>),
247 Pure {
248 bytes: &'a [u8],
249 metadata: &'a T::PureInput,
250 local: Local<'a>,
251 },
252 Receiving {
253 metadata: &'a T::ReceivingInput,
254 local: Local<'a>,
255 },
256}
257
258pub struct Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
260where
261 Mode: ExecutionMode,
262{
263 pub env: &'env Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
264 pub metrics: Arc<ExecutionMetrics>,
266 pub native_extensions: NativeExtensions<'env>,
267 pub tx_context: Rc<RefCell<TxContext>>,
270 pub gas_charger: &'gas mut GasCharger,
272 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
274 locations: Locations,
276 gas_coin_transfer: Option<GasCoinTransfer>,
278 executable_vm_cache: QCache<LinkageHash, MoveVM<'env>>,
280}
281
282impl Locations {
283 fn resolve(&mut self, location: T::Location) -> Result<ResolvedLocation<'_>, ExecutionError> {
286 Ok(match location {
287 T::Location::TxContext => ResolvedLocation::Local(self.tx_context_value.local(0)?),
288 T::Location::GasCoin => {
289 let (_, _, gas_locals) = unwrap!(self.gas.as_mut(), "Gas coin not provided");
290 ResolvedLocation::Local(gas_locals.local(0)?)
291 }
292 T::Location::ObjectInput(i) => ResolvedLocation::Local(self.object_inputs.local(i)?),
293 T::Location::WithdrawalInput(i) => {
294 ResolvedLocation::Local(self.withdrawal_inputs.local(i)?)
295 }
296 T::Location::Result(i, j) => {
297 let result = unwrap!(self.results.get_mut(i as usize), "bounds already verified");
298 ResolvedLocation::Local(result.local(j)?)
299 }
300 T::Location::PureInput(i) => {
301 let local = self.pure_inputs.local(i)?;
302 let metadata = &self.pure_input_metadata.safe_get(i as usize)?;
303 let bytes = self
304 .pure_input_bytes
305 .get_index(metadata.byte_index)
306 .ok_or_else(|| {
307 make_invariant_violation!(
308 "Pure input {} bytes out of bounds at index {}",
309 metadata.original_input_index.0,
310 metadata.byte_index,
311 )
312 })?;
313 ResolvedLocation::Pure {
314 bytes,
315 metadata,
316 local,
317 }
318 }
319 T::Location::ReceivingInput(i) => ResolvedLocation::Receiving {
320 metadata: self.receiving_input_metadata.safe_get(i as usize)?,
321 local: self.receiving_inputs.local(i)?,
322 },
323 })
324 }
325}
326
327impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
328 Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas, 'extension, Mode>
329where
330 Mode: ExecutionMode,
331{
332 #[instrument(name = "Context::new", level = "trace", skip_all)]
333 pub fn new(
334 env: &'env Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
335 metrics: Arc<ExecutionMetrics>,
336 tx_context: Rc<RefCell<TxContext>>,
337 gas_charger: &'gas mut GasCharger,
338 payment_location: Option<GasPayment>,
339 pure_input_bytes: IndexSet<Vec<u8>>,
340 object_inputs: Vec<T::ObjectInput>,
341 input_withdrawal_metadata: Vec<T::WithdrawalInput>,
342 pure_input_metadata: Vec<T::PureInput>,
343 receiving_input_metadata: Vec<T::ReceivingInput>,
344 ) -> Result<Self, Mode::Error>
345 where
346 'pc: 'state,
347 {
348 let mut input_object_map = BTreeMap::new();
349 let mut input_object_metadata = Vec::with_capacity(object_inputs.len());
350 let mut object_values = Vec::with_capacity(object_inputs.len());
351 for object_input in object_inputs {
352 let (i, m, v) = load_object_arg(gas_charger, env, &mut input_object_map, object_input)?;
353 input_object_metadata.push((i, m));
354 object_values.push(Some(v));
355 }
356 let object_inputs = Locals::new(object_values)?;
357 let mut withdrawal_values = Vec::with_capacity(input_withdrawal_metadata.len());
358 for withdrawal_input in &input_withdrawal_metadata {
359 let v = load_withdrawal_arg(gas_charger, env, withdrawal_input)?;
360 withdrawal_values.push(Some(v));
361 }
362 let withdrawal_inputs = Locals::new(withdrawal_values)?;
363 let pure_inputs = Locals::new_invalid(pure_input_metadata.len())?;
364 let receiving_inputs = Locals::new_invalid(receiving_input_metadata.len())?;
365 let mut new_gas_coin_id = None;
366 let gas = match payment_location {
367 Some(gas_payment)
368 if matches!(gas_payment.location, PaymentLocation::AddressBalance(_))
369 && !env.protocol_config.gasless_transaction_drop_safety() =>
370 {
371 None
372 }
373 Some(gas_payment) => {
374 let ty = env.gas_coin_type()?;
375 let (gas_metadata, gas_value) = match gas_payment.location {
376 PaymentLocation::AddressBalance(sui_address) => {
377 assert_invariant!(
378 env.protocol_config.enable_address_balance_gas_payments(),
379 "Address balance gas payments must be enabled to have an address \
380 balance payment location"
381 );
382 let max_gas_in_balance = gas_charger.gas_budget();
383 assert_invariant!(
384 gas_payment.amount >= max_gas_in_balance,
385 "not enough gas to pay. How did we get this far?"
386 );
387 let id = tx_context.borrow_mut().fresh_id();
388 new_gas_coin_id = Some(id);
389
390 let metadata = InputObjectMetadata {
391 newly_created: true,
392 id,
393 refined_permissions: ObjectPermissions::ALL,
394 owner: Owner::AddressOwner(sui_address),
395 version: SequenceNumber::new(),
396 type_: ty,
397 };
398 let coin = Value::coin(id, gas_payment.amount);
399 (metadata, coin)
400 }
401 PaymentLocation::Coin(gas_coin_id) => load_object_arg_impl(
402 gas_charger,
403 env,
404 &mut input_object_map,
405 gas_coin_id,
406 ObjectPermissions::ALL,
407 ty,
408 )?,
409 };
410 let mut gas_locals = Locals::new([Some(gas_value)])?;
411 let mut gas_local = gas_locals.local(0)?;
412 let gas_ref = gas_local.borrow()?;
413 let max_gas_in_balance = gas_charger.gas_budget();
415 gas_ref.coin_ref_subtract_balance(max_gas_in_balance)?;
416 Some((gas_payment, gas_metadata, gas_locals))
417 }
418 None => None,
419 };
420 let native_extensions = adapter::new_native_extensions(
421 env.state_view,
422 env.state_view,
423 input_object_map,
424 !gas_charger.is_unmetered(),
425 env.protocol_config,
426 metrics.clone(),
427 tx_context.clone(),
428 )?;
429 if let Some(new_gas_coin_id) = new_gas_coin_id {
430 native_extensions
433 .try_borrow_mut()
434 .map_err(|_| {
435 make_invariant_violation!(
436 "Should be able to borrow object runtime native extension"
437 )
438 })?
439 .get_mut::<ObjectRuntime>()
440 .and_then(|object_runtime| object_runtime.new_id(new_gas_coin_id))
441 .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
442 }
443
444 debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
445 let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
446 tx_context.borrow().digest(),
447 )?)])?;
448 Ok(Self {
449 env,
450 metrics,
451 native_extensions,
452 tx_context,
453 gas_charger,
454 user_events: vec![],
455 locations: Locations {
456 tx_context_value,
457 gas,
458 input_object_metadata,
459 object_inputs,
460 input_withdrawal_metadata,
461 withdrawal_inputs,
462 pure_input_bytes,
463 pure_input_metadata,
464 pure_inputs,
465 receiving_input_metadata,
466 receiving_inputs,
467 results: vec![],
468 },
469 gas_coin_transfer: None,
470 executable_vm_cache: QCache::new(1024),
471 })
472 }
473
474 pub(crate) fn record_gas_coin_transfer(
475 &mut self,
476 transfer: GasCoinTransfer,
477 ) -> Result<(), Mode::Error> {
478 assert_invariant!(
480 !matches!(transfer, GasCoinTransfer::SendFunds { .. })
481 || self.env.protocol_config.enable_accumulators(),
482 "Gas coin transfers with send_funds are not allowed unless accumulators are enabled"
483 );
484 if self.gas_coin_transfer.is_some() {
485 invariant_violation!("Gas coin destination set more than once");
486 }
487 self.gas_coin_transfer = Some(transfer);
488 Ok(())
489 }
490
491 pub fn finish(mut self) -> Result<ExecutionResults, Mode::Error> {
492 assert_invariant!(
493 !self.locations.tx_context_value.local(0)?.is_invalid()?,
494 "tx context value should be present"
495 );
496 let gas_coin_transfer = self.gas_coin_transfer;
497 let gas = std::mem::take(&mut self.locations.gas);
498 let object_input_metadata = std::mem::take(&mut self.locations.input_object_metadata);
499 let mut object_inputs =
500 std::mem::replace(&mut self.locations.object_inputs, Locals::new_invalid(0)?);
501 let mut created_input_object_ids = BTreeSet::new();
502 let mut loaded_runtime_objects = BTreeMap::new();
503 let mut by_value_shared_objects = BTreeSet::new();
504 let mut consensus_owner_objects = BTreeMap::new();
505 let mut gas_payment_location = None;
506 let gas = gas
507 .map(|(payment_location, m, mut g)| {
508 gas_payment_location = Some(payment_location);
509 let value_opt = g.local(0)?.move_if_valid()?;
510 let moved = value_opt.is_none();
511 assert_invariant!(
512 moved == gas_coin_transfer.is_some(),
513 "Gas coin moved requires gas coin transfer to be recorded, and vice versa"
514 );
515 Result::<_, ExecutionError>::Ok((m, value_opt))
516 })
517 .transpose()?;
518
519 let gas_id_opt = gas.as_ref().map(|(m, _)| m.id);
520 let object_inputs = object_input_metadata
521 .into_iter()
522 .enumerate()
523 .map(|(i, (_, m))| {
524 let v_opt = object_inputs.local(checked_as!(i, u16)?)?.move_if_valid()?;
525 Ok((m, v_opt))
526 })
527 .collect::<Result<Vec<_>, ExecutionError>>()?;
528 for (metadata, value_opt) in object_inputs.into_iter().chain(gas) {
529 let InputObjectMetadata {
530 newly_created,
531 id,
532 refined_permissions,
533 owner,
534 version,
535 type_,
536 } = metadata;
537 if !refined_permissions.can_use_mutably() {
538 continue;
539 }
540
541 if newly_created {
542 created_input_object_ids.insert(id);
543 } else {
544 loaded_runtime_objects.insert(
545 id,
546 LoadedRuntimeObject {
547 version,
548 is_modified: true,
549 },
550 );
551 }
552 if let Some(object) = value_opt {
553 self.transfer_object_(
554 owner,
555 type_,
556 CtxValue(object),
557 true,
558 )?;
559 } else if owner.is_shared() {
560 by_value_shared_objects.insert(id);
561 } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
562 consensus_owner_objects.insert(id, owner.clone());
563 }
564 }
565
566 let Self {
567 env,
568 native_extensions,
569 tx_context,
570 gas_charger,
571 user_events,
572 ..
573 } = self;
574 let ref_context: &RefCell<TxContext> = &tx_context;
575 let tx_context: &TxContext = &ref_context.borrow();
576 let tx_digest = ref_context.borrow().digest();
577
578 let object_runtime: ObjectRuntime = native_extensions
579 .try_borrow_mut()
580 .map_err(|_| {
581 make_invariant_violation!(
582 "Should be able to borrow object runtime native extension at the end of execution"
583 )
584 })?
585 .remove()
586 .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
587
588 let RuntimeResults {
589 mut writes,
590 user_events: remaining_events,
591 loaded_child_objects,
592 mut created_object_ids,
593 deleted_object_ids,
594 mut accumulator_events,
595 settlement_input_sui,
596 settlement_output_sui,
597 } = object_runtime.finish()?;
598 assert_invariant!(
599 loaded_runtime_objects
600 .keys()
601 .all(|id| !created_object_ids.contains(id)),
602 "Loaded input objects should not be in the created objects set"
603 );
604 assert_invariant!(
607 remaining_events.is_empty(),
608 "Events should be taken after every Move call"
609 );
610 if let Some(gas_id) = gas_id_opt {
612 assert_invariant!(
614 !deleted_object_ids.contains(&gas_id)
615 || gas_coin_transfer.is_some_and(|destination| matches!(
616 destination,
617 GasCoinTransfer::SendFunds { .. }
618 )),
619 "Gas coin should not be deleted"
620 );
621 let Some(gas_payment_location) = gas_payment_location else {
622 invariant_violation!("Gas payment should be specified if gas ID is present");
623 };
624 finish_gas_coin(
625 gas_charger,
626 &mut writes,
627 &mut created_object_ids,
628 &deleted_object_ids,
629 &mut accumulator_events,
630 gas_id,
631 gas_payment_location,
632 gas_coin_transfer,
633 )?;
634 }
635
636 loaded_runtime_objects.extend(loaded_child_objects);
637
638 let mut written_objects = BTreeMap::new();
639
640 let (writeout_vm, ty_linkage) =
641 Self::make_writeout_vm(env, writes.values().map(|(_, ty, _)| ty.clone()))?;
642
643 for (id, (recipient, ty, value)) in writes {
644 let (ty, layout) = Self::load_type_and_layout_from_struct_for_writeout(
645 env,
646 &writeout_vm,
647 &ty_linkage,
648 ty.clone().into(),
649 )?;
650 let abilities = ty.abilities();
651 let has_public_transfer = abilities.has_store();
652 let Some(bytes) = value.typed_serialize(&layout) else {
653 invariant_violation!("Failed to serialize already deserialized Move value");
654 };
655 let move_object = unsafe {
657 create_written_object::<Mode>(
658 env,
659 &loaded_runtime_objects,
660 id,
661 ty,
662 has_public_transfer,
663 bytes,
664 )?
665 };
666 let object = Object::new_move(move_object, recipient, tx_digest);
667 written_objects.insert(id, object);
668 }
669
670 for package in self
671 .env
672 .linkable_store
673 .package_store
674 .to_new_packages()
675 .into_iter()
676 {
677 let package_obj = Object::new_from_package(package, tx_digest);
678 let id = package_obj.id();
679 created_object_ids.insert(id);
680 written_objects.insert(id, package_obj);
681 }
682
683 Ok(execution::context::finish(
684 env.protocol_config,
685 env.state_view,
686 gas_charger,
687 tx_context,
688 &by_value_shared_objects,
689 &consensus_owner_objects,
690 loaded_runtime_objects,
691 written_objects,
692 created_object_ids,
693 deleted_object_ids,
694 user_events,
695 accumulator_events,
696 settlement_input_sui,
697 settlement_output_sui,
698 )?)
699 }
700
701 pub fn take_user_events(
702 &mut self,
703 vm: &MoveVM<'_>,
704 version_mid: ModuleId,
705 function_def_idx: FunctionDefinitionIndex,
706 instr_length: u16,
707 linkage: &ExecutableLinkage,
708 ) -> Result<(), Mode::Error> {
709 let events = object_runtime_mut!(self)?.take_user_events();
710 let Some(num_events) = self.user_events.len().checked_add(events.len()) else {
711 invariant_violation!("usize overflow, too many events emitted")
712 };
713 let max_events = self.env.protocol_config.max_num_event_emit();
714 if num_events as u64 > max_events {
715 let err = max_event_error(max_events)
716 .at_code_offset(function_def_idx, instr_length)
717 .finish(Location::Module(version_mid.clone()));
718 return Err(self.env.convert_linked_vm_error(err, linkage));
719 }
720 let new_events = events
721 .into_iter()
722 .map(|(tag, value)| {
723 let type_tag = TypeTag::Struct(Box::new(tag));
724 let layout = vm
725 .runtime_type_layout(&type_tag)
726 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
727 let Some(bytes) = value.typed_serialize(&layout) else {
728 invariant_violation!("Failed to serialize Move event");
729 };
730 let TypeTag::Struct(tag) = type_tag else {
731 unreachable!()
732 };
733 Ok((version_mid.clone(), *tag, bytes))
734 })
735 .collect::<Result<Vec<_>, Mode::Error>>()?;
736 self.user_events.extend(new_events);
737 Ok(())
738 }
739
740 fn make_writeout_vm<I>(
749 env: &Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
750 writes: I,
751 ) -> Result<(MoveVM<'extension>, ExecutableLinkage), Mode::Error>
752 where
753 I: IntoIterator<Item = MoveObjectType>,
754 {
755 let tys_addrs = writes
756 .into_iter()
757 .flat_map(|ty| StructTag::from(ty).all_addresses())
758 .map(ObjectID::from)
759 .collect::<BTreeSet<_>>();
760
761 let ty_linkage = ExecutableLinkage::type_linkage::<_, Mode::Error>(
762 env.linkage_analysis.config().clone(),
763 &tys_addrs,
764 env.linkable_store,
765 )?;
766 env.vm
767 .make_vm(
768 &env.linkable_store.package_store,
769 ty_linkage.linkage_context::<Mode::Error>()?,
770 )
771 .map_err(|e| env.convert_linked_vm_error(e, &ty_linkage))
772 .map(|vm| (vm, ty_linkage))
773 }
774
775 fn load_type_and_layout_from_struct_for_writeout(
780 env: &Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
781 vm: &MoveVM,
782 linkage: &ExecutableLinkage,
783 tag: StructTag,
784 ) -> Result<(Type, move_core_types::runtime_value::MoveTypeLayout), Mode::Error> {
785 let type_tag = TypeTag::Struct(Box::new(tag));
786 let vm_type = vm
787 .load_type(&type_tag)
788 .map_err(|e| env.convert_linked_vm_error(e, linkage))?;
789 let layout = vm
790 .runtime_type_layout(&type_tag)
791 .map_err(|e| env.convert_vm_error(e))?;
792 env.adapter_type_from_vm_type(vm, &vm_type)
793 .map(|ty| (ty, layout))
794 }
795
796 fn location(&mut self, usage: UsageKind, location: T::Location) -> Result<Value, Mode::Error> {
801 let resolved = self.locations.resolve(location)?;
802 let mut local = match resolved {
803 ResolvedLocation::Local(l) => l,
804 ResolvedLocation::Pure {
805 bytes,
806 metadata,
807 mut local,
808 } => {
809 if local.is_invalid()? {
810 let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
811 local.store(v)?;
812 }
813 local
814 }
815 ResolvedLocation::Receiving {
816 metadata,
817 mut local,
818 } => {
819 if local.is_invalid()? {
820 let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
821 local.store(v)?;
822 }
823 local
824 }
825 };
826 Ok(match usage {
827 UsageKind::Move => {
828 let value = local.move_()?;
829 charge_gas_!(self.gas_charger, self.env, charge_move_loc, &value)?;
830 value
831 }
832 UsageKind::Copy => {
833 let value = local.copy()?;
834 charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
835 value
836 }
837 UsageKind::Borrow => {
838 charge_gas_!(
839 self.gas_charger,
840 self.env,
841 charge_simple_instr(SimpleInstruction::MutBorrowLoc)
842 )?;
843 local.borrow()?
844 }
845 })
846 }
847
848 fn location_usage(&mut self, usage: T::Usage) -> Result<Value, Mode::Error> {
849 match usage {
850 T::Usage::Move(location) => self.location(UsageKind::Move, location),
851 T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
852 }
853 }
854
855 fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, Mode::Error> {
856 match arg_ {
857 T::Argument__::Use(usage) => self.location_usage(usage),
858 T::Argument__::Freeze(usage) => self.location_usage(usage),
860 T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
861 T::Argument__::Read(usage) => {
862 let reference = self.location_usage(usage)?;
863 charge_gas!(self, charge_read_ref, &reference)?;
864 Ok(reference.read_ref()?)
865 }
866 }
867 }
868
869 pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, Mode::Error>
870 where
871 VMValue: VMValueCast<V>,
872 {
873 let before_height = self.gas_charger.move_gas_status().stack_height_current();
874 let value = self.argument_value(arg)?;
875 let after_height = self.gas_charger.move_gas_status().stack_height_current();
876 debug_assert_eq!(before_height.saturating_add(1), after_height);
877 let value: V = value.cast()?;
878 Ok(value)
879 }
880
881 pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, Mode::Error>
882 where
883 VMValue: VMValueCast<V>,
884 {
885 args.into_iter().map(|arg| self.argument(arg)).collect()
886 }
887
888 pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), Mode::Error> {
889 self.locations
890 .results
891 .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
892 Ok(())
893 }
894
895 pub fn charge_command(
896 &mut self,
897 is_move_call: bool,
898 num_args: usize,
899 num_return: usize,
900 ) -> Result<(), Mode::Error> {
901 let move_gas_status = self.gas_charger.move_gas_status_mut();
902 let before_size = move_gas_status.stack_size_current();
903 let num_popped = if is_move_call {
907 num_args.checked_add(num_return).ok_or_else(|| {
908 make_invariant_violation!("usize overflow when charging gas for command",)
909 })?
910 } else {
911 num_args
912 };
913 move_gas_status
914 .charge(1, 0, num_popped as u64, 0, 1)
915 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
916 let after_size = move_gas_status.stack_size_current();
917 assert_invariant!(
918 before_size == after_size,
919 "We assume currently that the stack size is not decremented. \
920 If this changes, we need to actually account for it here"
921 );
922 Ok(())
923 }
924
925 pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, Mode::Error> {
926 Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
927 }
928
929 pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, Mode::Error> {
930 let id = self.tx_context.borrow_mut().fresh_id();
931 object_runtime_mut!(self)?
932 .new_id(id)
933 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
934 Ok(CtxValue(Value::coin(id, amount)))
935 }
936
937 pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, Mode::Error> {
938 let (id, amount) = coin.0.unpack_coin()?;
939 object_runtime_mut!(self)?
940 .delete_id(id)
941 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
942 Ok(amount)
943 }
944
945 pub fn new_upgrade_cap(&mut self, version_id: ObjectID) -> Result<CtxValue, Mode::Error> {
946 let id = self.tx_context.borrow_mut().fresh_id();
947 object_runtime_mut!(self)?
948 .new_id(id)
949 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
950 let cap = UpgradeCap::new(id, version_id);
951 Ok(CtxValue(Value::upgrade_cap(cap)))
952 }
953
954 pub fn upgrade_receipt(
955 &self,
956 upgrade_ticket: UpgradeTicket,
957 upgraded_package_id: ObjectID,
958 ) -> CtxValue {
959 let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
960 CtxValue(Value::upgrade_receipt(receipt))
961 }
962
963 pub fn vm_move_call(
968 &mut self,
969 function: T::LoadedFunction,
970 args: Vec<CtxValue>,
971 trace_builder_opt: &mut Option<MoveTraceBuilder>,
972 ) -> Result<Vec<CtxValue>, Mode::Error> {
973 with_vm!(self, &function.linkage, Mode::Error, |vm: &mut MoveVM<
974 'env,
975 >| {
976 let ty_args = function
977 .type_arguments
978 .iter()
979 .map(|ty| {
980 let tag: TypeTag = ty.clone().try_into().map_err(|e| {
981 Mode::Error::new_with_source(ExecutionErrorKind::VMInvariantViolation, e)
982 })?;
983 vm.load_type(&tag)
984 .map_err(|e| self.env.convert_linked_vm_error(e, &function.linkage))
985 })
986 .collect::<Result<Vec<_>, Mode::Error>>()?;
987 let max_type_nodes = is_settle_u128_call(
988 function.original_mid.address(),
989 function.original_mid.name(),
990 &function.name,
991 )
992 .then_some(SETTLEMENT_MAX_TYPE_INSTANTIATION_NODES);
993 let result = self.execute_function_bypass_visibility_with_vm(
994 vm,
995 &function.original_mid,
996 &function.name,
997 ty_args,
998 args,
999 &function.linkage,
1000 trace_builder_opt,
1001 max_type_nodes,
1002 )?;
1003 self.take_user_events(
1004 vm,
1005 function.version_mid,
1006 function.definition_index,
1007 function.instruction_length,
1008 &function.linkage,
1009 )?;
1010 Ok::<Vec<CtxValue>, Mode::Error>(result)
1011 })
1012 }
1013
1014 fn execute_function_bypass_visibility_with_vm(
1015 &mut self,
1016 vm: &mut MoveVM<'env>,
1017 original_mid: &ModuleId,
1018 function_name: &IdentStr,
1019 ty_args: Vec<VMType>,
1020 args: Vec<CtxValue>,
1021 linkage: &ExecutableLinkage,
1022 tracer: &mut Option<MoveTraceBuilder>,
1023 max_type_nodes: Option<u64>,
1024 ) -> Result<Vec<CtxValue>, Mode::Error> {
1025 let gas_status = self.gas_charger.move_gas_status_mut();
1026 let values = vm
1027 .execute_function_bypass_visibility_with_max_type_nodes(
1028 original_mid,
1029 function_name,
1030 ty_args,
1031 args.into_iter().map(|v| v.0.into()).collect(),
1032 &mut SuiGasMeter(gas_status),
1033 tracer.as_mut(),
1034 max_type_nodes,
1035 )
1036 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
1037 Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
1038 }
1039
1040 pub fn deserialize_package(
1045 &mut self,
1046 package_payload: PackagePayload,
1047 dep_ids: &[ObjectID],
1048 ) -> Result<DeserializedPackage, Mode::Error> {
1049 Ok(match package_payload {
1050 PackagePayload::Deserialized(deserialized_pkg) => deserialized_pkg,
1051 PackagePayload::Serialized(module_bytes) => {
1052 assert_invariant!(
1056 !module_bytes.is_empty(),
1057 "empty package is checked in transaction input checker"
1058 );
1059 let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
1060 self.gas_charger.charge_publish_package(total_bytes)?;
1061 self.env.deserialize_package(&module_bytes, dep_ids)?
1062 }
1063 })
1064 }
1065
1066 fn fetch_package(&mut self, dependency_id: &ObjectID) -> Result<Rc<MovePackage>, Mode::Error> {
1067 let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
1068 |_| {
1069 make_invariant_violation!(
1070 "We should always fetch a single package for each object or return a dependency error."
1071 )
1072 },
1073 )?;
1074 Ok(fetched_package)
1075 }
1076
1077 fn fetch_packages(
1078 &mut self,
1079 dependency_ids: &[ObjectID],
1080 ) -> Result<Vec<Rc<MovePackage>>, Mode::Error> {
1081 let mut fetched = vec![];
1082 let mut missing = vec![];
1083
1084 let dependency_ids: BTreeSet<_> = dependency_ids.iter().collect();
1086
1087 for id in &dependency_ids {
1088 match self.env.linkable_store.get_move_package(id) {
1089 Err(e) => {
1090 return Err(Mode::Error::new_with_source(
1091 ExecutionErrorKind::PublishUpgradeMissingDependency,
1092 e,
1093 ));
1094 }
1095 Ok(Some(inner)) => {
1096 fetched.push(inner);
1097 }
1098 Ok(None) => {
1099 missing.push(*id);
1100 }
1101 }
1102 }
1103
1104 if missing.is_empty() {
1105 assert_invariant!(
1106 fetched.len() == dependency_ids.len(),
1107 "all dependencies should be fetched"
1108 );
1109 Ok(fetched)
1110 } else {
1111 let msg = format!(
1112 "Missing dependencies: {}",
1113 missing
1114 .into_iter()
1115 .map(|dep| format!("{}", dep))
1116 .collect::<Vec<_>>()
1117 .join(", ")
1118 );
1119 Err(Mode::Error::new_with_source(
1120 ExecutionErrorKind::PublishUpgradeMissingDependency,
1121 msg,
1122 ))
1123 }
1124 }
1125
1126 fn publish_and_verify_modules(
1127 &mut self,
1128 package_id: ObjectID,
1129 pkg: &MovePackage,
1130 modules: &[CompiledModule],
1131 linkage: &ExecutableLinkage,
1132 ) -> Result<(VerifiedPackage, MoveVM<'env>), Mode::Error> {
1133 let serialized_pkg = pkg.into_serialized_move_package().map_err(|e| {
1134 make_invariant_violation!("Failed to serialize package for verification: {}", e)
1135 })?;
1136 let data_store = &self.env.linkable_store.package_store;
1137 let vm = self
1138 .env
1139 .vm
1140 .validate_package(
1141 data_store,
1142 *package_id,
1143 serialized_pkg,
1144 &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
1145 self.native_extensions.clone(),
1146 )
1147 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
1148
1149 for module in modules {
1151 sui_verifier::verifier::sui_verify_module_unmetered(
1154 module,
1155 &BTreeMap::new(),
1156 &self
1157 .env
1158 .protocol_config
1159 .verifier_config(None),
1160 )?;
1161 }
1162
1163 Ok(vm)
1164 }
1165
1166 fn push_package_and_init_selected_modules<'a>(
1172 &mut self,
1173 package_id: ObjectID,
1174 package: MovePackage,
1175 verified_pkg: VerifiedPackage,
1176 vm: MoveVM<'env>,
1177 modules: impl IntoIterator<Item = &'a CompiledModule>,
1178 expected_inits: BTreeSet<Identifier>,
1179 linkage: &ExecutableLinkage,
1180 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1181 expected_stack_height: u64,
1182 ) -> Result<(), Mode::Error> {
1183 self.env.linkable_store.package_store.push_package(
1184 package_id,
1185 Rc::new(package),
1186 verified_pkg,
1187 )?;
1188
1189 match self.init_selected_modules(
1190 vm,
1191 package_id,
1192 modules,
1193 expected_inits,
1194 linkage,
1195 trace_builder_opt,
1196 expected_stack_height,
1197 ) {
1198 Ok(()) => Ok(()),
1199 Err(e) => {
1200 self.env
1201 .linkable_store
1202 .package_store
1203 .pop_package(package_id)?;
1204 Err(e)
1205 }
1206 }
1207 }
1208
1209 fn init_selected_modules<'a>(
1210 &mut self,
1211 mut vm: MoveVM<'env>,
1212 package_id: ObjectID,
1213 modules: impl IntoIterator<Item = &'a CompiledModule>,
1214 mut expected_inits: BTreeSet<Identifier>,
1215 linkage: &ExecutableLinkage,
1216 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1217 expected_stack_height: u64,
1218 ) -> Result<(), Mode::Error> {
1219 debug_assert_eq!(
1220 self.gas_charger.move_gas_status().stack_height_current(),
1221 expected_stack_height,
1222 );
1223 let check_expected_inits = self.env.protocol_config.harden_linkage_consistency();
1224 for module in modules {
1225 let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
1226 else {
1227 continue;
1228 };
1229 let module_name = module.identifier_at(module.self_handle().name);
1230 let fhandle = module.function_handle_at(fdef.function);
1231 let fparameters = module.signature_at(fhandle.parameters);
1232 assert_invariant!(
1233 fparameters.0.len() <= 2,
1234 "init function should have at most 2 parameters"
1235 );
1236 let has_otw = fparameters.0.len() == 2;
1237 let tx_context = self
1238 .location(UsageKind::Borrow, T::Location::TxContext)
1239 .map_err(|e| {
1240 make_invariant_violation!("Failed to get tx context for init function: {}", e)
1241 })?;
1242 charge_gas!(self, charge_store_loc, &tx_context)?;
1244
1245 if check_expected_inits {
1246 assert_invariant!(
1247 expected_inits.remove(module_name),
1248 "module {module_name} defines an `init` but was not recorded as doing so when \
1249 the package payload was deserialized"
1250 );
1251 }
1252
1253 let args = if has_otw {
1254 vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
1255 } else {
1256 vec![CtxValue(tx_context)]
1257 };
1258 debug_assert_eq!(
1259 self.gas_charger.move_gas_status().stack_height_current(),
1260 expected_stack_height,
1261 );
1262 trace_utils::trace_move_call_start(trace_builder_opt);
1263 let return_values = self.execute_function_bypass_visibility_with_vm(
1264 &mut vm,
1265 &module.self_id(),
1266 INIT_FN_NAME,
1267 vec![],
1268 args,
1269 linkage,
1270 trace_builder_opt,
1271 None,
1272 )?;
1273 trace_utils::trace_move_call_end(trace_builder_opt);
1274
1275 let version_mid = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
1276 self.take_user_events(
1277 &vm,
1278 version_mid,
1279 fdef_idx,
1280 fdef.code
1281 .as_ref()
1282 .map(|c| checked_as!(c.code.len(), u16))
1283 .transpose()?
1284 .unwrap_or(0),
1285 linkage,
1286 )?;
1287 assert_invariant!(
1288 return_values.is_empty(),
1289 "init should not have return values"
1290 );
1291 debug_assert_eq!(
1292 self.gas_charger.move_gas_status().stack_height_current(),
1293 expected_stack_height,
1294 );
1295 }
1296
1297 assert_invariant!(
1299 !check_expected_inits || expected_inits.is_empty(),
1300 "modules {expected_inits:?} define an `init` that was never run"
1301 );
1302
1303 Ok(())
1304 }
1305
1306 pub fn publish_and_init_package(
1307 &mut self,
1308 package_payload: DeserializedPackage,
1309 dep_ids: &[ObjectID],
1310 linkage: ResolvedLinkage,
1311 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1312 ) -> Result<ObjectID, Mode::Error> {
1313 let DeserializedPackage {
1314 deserialized_modules: mut modules,
1315 modules_with_init,
1316 ..
1317 } = package_payload;
1318 let original_id = if Mode::packages_are_predefined() {
1319 (*modules.safe_get(0)?.self_id().address()).into()
1321 } else {
1322 let id = self.tx_context.borrow_mut().fresh_id();
1326 adapter::substitute_package_id(&mut modules, id)?;
1327 id
1328 };
1329
1330 let dependencies = self.fetch_packages(dep_ids)?;
1331 let package = MovePackage::new_initial(
1332 &modules,
1333 self.env.protocol_config,
1334 dependencies.iter().map(|p| p.as_ref()),
1335 )?;
1336 let package_id = package.id();
1337
1338 let linkage = ResolvedLinkage::update_for_publication(package_id, original_id, linkage);
1339
1340 let (pkg, vm) =
1341 self.publish_and_verify_modules(original_id, &package, &modules, &linkage)?;
1342 self.push_package_and_init_selected_modules(
1343 package_id,
1344 package,
1345 pkg,
1346 vm,
1347 &modules,
1348 modules_with_init,
1349 &linkage,
1350 trace_builder_opt,
1351 PUBLISH_INIT_EXPECTED_STACK_HEIGHT,
1352 )?;
1353 Ok(original_id)
1354 }
1355
1356 pub fn upgrade(
1357 &mut self,
1358 package_payload: DeserializedPackage,
1359 dep_ids: &[ObjectID],
1360 current_package_id: ObjectID,
1361 upgrade_ticket_policy: u8,
1362 linkage: ResolvedLinkage,
1363 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1364 ) -> Result<ObjectID, Mode::Error> {
1365 let DeserializedPackage {
1366 deserialized_modules: mut modules,
1367 modules_with_init,
1368 ..
1369 } = package_payload;
1370 let current_move_package = self.fetch_package(¤t_package_id)?;
1372
1373 let original_id = current_move_package.original_package_id();
1374 adapter::substitute_package_id(&mut modules, original_id)?;
1375
1376 let version_id = self.tx_context.borrow_mut().fresh_id();
1381
1382 let dependencies = self.fetch_packages(dep_ids)?;
1383 let package = current_move_package.new_upgraded(
1384 version_id,
1385 &modules,
1386 self.env.protocol_config,
1387 dependencies.iter().map(|p| p.as_ref()),
1388 )?;
1389
1390 let linkage = ResolvedLinkage::update_for_publication(version_id, original_id, linkage);
1391 let (verified_pkg, vm) =
1392 self.publish_and_verify_modules(original_id, &package, &modules, &linkage)?;
1393
1394 check_compatibility(
1395 self.env.protocol_config,
1396 current_move_package.as_ref(),
1397 &modules,
1398 upgrade_ticket_policy,
1399 )?;
1400
1401 let current_module_names: BTreeSet<&str> = current_move_package
1404 .serialized_module_map()
1405 .keys()
1406 .map(|s| s.as_str())
1407 .collect();
1408 let new_modules = modules
1409 .iter()
1410 .filter(|m| {
1411 let name = m.identifier_at(m.self_handle().name).as_str();
1412 !current_module_names.contains(name)
1413 })
1414 .collect::<Vec<&CompiledModule>>();
1415
1416 if self.env.protocol_config.enable_init_on_upgrade() {
1417 let expected_inits: BTreeSet<Identifier> = modules_with_init
1419 .into_iter()
1420 .filter(|name| !current_module_names.contains(name.as_str()))
1421 .collect();
1422 self.push_package_and_init_selected_modules(
1423 version_id,
1424 package,
1425 verified_pkg,
1426 vm,
1427 new_modules.iter().copied(),
1428 expected_inits,
1429 &linkage,
1430 trace_builder_opt,
1431 UPGRADE_INIT_EXPECTED_STACK_HEIGHT,
1432 )?;
1433 } else {
1434 let new_module_has_init = new_modules.iter().any(|module| {
1435 module.function_defs.iter().any(|fdef| {
1436 let fhandle = module.function_handle_at(fdef.function);
1437 let fname = module.identifier_at(fhandle.name);
1438 fname == INIT_FN_NAME
1439 })
1440 });
1441 if new_module_has_init {
1442 return Err(Mode::Error::new_with_source(
1443 ExecutionErrorKind::FeatureNotYetSupported,
1444 "`init` in new modules on upgrade is not yet supported",
1445 ));
1446 }
1447
1448 self.env.linkable_store.package_store.push_package(
1449 version_id,
1450 Rc::new(package),
1451 verified_pkg,
1452 )?;
1453 }
1454
1455 Ok(version_id)
1456 }
1457
1458 pub fn transfer_object(
1463 &mut self,
1464 recipient: Owner,
1465 ty: Type,
1466 object: CtxValue,
1467 ) -> Result<(), Mode::Error> {
1468 self.transfer_object_(recipient, ty, object, false)
1469 }
1470
1471 fn transfer_object_(
1472 &mut self,
1473 recipient: Owner,
1474 ty: Type,
1475 object: CtxValue,
1476 end_of_transaction: bool,
1477 ) -> Result<(), Mode::Error> {
1478 let tag = TypeTag::try_from(ty)
1479 .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1480 let TypeTag::Struct(tag) = tag else {
1481 invariant_violation!("Expected struct type tag");
1482 };
1483 let ty = MoveObjectType::from(*tag);
1484 object_runtime_mut!(self)?
1485 .transfer(recipient, ty, object.0.into(), end_of_transaction)
1486 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1487 Ok(())
1488 }
1489
1490 #[allow(clippy::type_complexity)]
1495 pub fn argument_updates(
1496 &mut self,
1497 args: Vec<T::Argument>,
1498 ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, Mode::Error> {
1499 args.into_iter()
1500 .filter_map(|arg| self.argument_update(arg).transpose())
1501 .collect()
1502 }
1503
1504 #[allow(clippy::type_complexity)]
1505 fn argument_update(
1506 &mut self,
1507 sp!(_, (arg, ty)): T::Argument,
1508 ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, Mode::Error> {
1509 use sui_types::transaction::Argument as TxArgument;
1510 let ty = match ty {
1511 Type::Reference(true, inner) => (*inner).clone(),
1512 ty => {
1513 debug_assert!(
1514 false,
1515 "Unexpected non reference type in location update: {ty:?}"
1516 );
1517 return Ok(None);
1518 }
1519 };
1520 let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1521 invariant_violation!("unable to generate type tag from type")
1522 };
1523 let location = arg.location();
1524 let resolved = self.locations.resolve(location)?;
1525 let local = match resolved {
1526 ResolvedLocation::Local(local)
1527 | ResolvedLocation::Pure { local, .. }
1528 | ResolvedLocation::Receiving { local, .. } => local,
1529 };
1530 if local.is_invalid()? {
1531 return Ok(None);
1532 }
1533 let value = local.copy()?;
1535 let value = match arg {
1536 T::Argument__::Use(_) => {
1537 value.read_ref()?
1539 }
1540 T::Argument__::Borrow(_, _) => {
1541 value
1543 }
1544 T::Argument__::Freeze(_) => {
1545 invariant_violation!("freeze should not be used for a mutable reference")
1546 }
1547 T::Argument__::Read(_) => {
1548 invariant_violation!("read should not return a reference")
1549 }
1550 };
1551 let layout = self.env.runtime_layout(&ty)?;
1552 let Some(bytes) = value.typed_serialize(&layout) else {
1553 invariant_violation!("Failed to serialize Move value");
1554 };
1555 let arg = match location {
1556 T::Location::TxContext => return Ok(None),
1557 T::Location::GasCoin => TxArgument::GasCoin,
1558 T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1559 T::Location::ObjectInput(i) => TxArgument::Input(
1560 self.locations
1561 .input_object_metadata
1562 .safe_get(i as usize)?
1563 .0
1564 .0,
1565 ),
1566 T::Location::WithdrawalInput(i) => TxArgument::Input(
1567 self.locations
1568 .input_withdrawal_metadata
1569 .safe_get(i as usize)?
1570 .original_input_index
1571 .0,
1572 ),
1573 T::Location::PureInput(i) => TxArgument::Input(
1574 self.locations
1575 .pure_input_metadata
1576 .safe_get(i as usize)?
1577 .original_input_index
1578 .0,
1579 ),
1580 T::Location::ReceivingInput(i) => TxArgument::Input(
1581 self.locations
1582 .receiving_input_metadata
1583 .safe_get(i as usize)?
1584 .original_input_index
1585 .0,
1586 ),
1587 };
1588 Ok(Some((arg, bytes, tag)))
1589 }
1590
1591 pub fn tracked_results(
1592 &self,
1593 results: &[CtxValue],
1594 result_tys: &T::ResultType,
1595 ) -> Result<Vec<(Vec<u8>, TypeTag)>, Mode::Error> {
1596 assert_invariant!(
1597 results.len() == result_tys.len(),
1598 "results and result types should match"
1599 );
1600 results
1601 .iter()
1602 .zip_debug_eq(result_tys)
1603 .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1604 .collect()
1605 }
1606
1607 fn tracked_result(&self, result: &Value, ty: Type) -> Result<(Vec<u8>, TypeTag), Mode::Error> {
1608 let inner_value;
1609 let (v, ty) = match ty {
1610 Type::Reference(_, inner) => {
1611 inner_value = result.copy()?.read_ref()?;
1612 (&inner_value, (*inner).clone())
1613 }
1614 _ => (result, ty),
1615 };
1616 let layout = self.env.runtime_layout(&ty)?;
1617 let Some(bytes) = v.typed_serialize(&layout) else {
1618 invariant_violation!("Failed to serialize Move value");
1619 };
1620 let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1621 invariant_violation!("unable to generate type tag from type")
1622 };
1623 Ok((bytes, tag))
1624 }
1625}
1626
1627impl VMValueCast<CtxValue> for VMValue {
1628 fn cast(self) -> Result<CtxValue, PartialVMError> {
1629 Ok(CtxValue(self.into()))
1630 }
1631}
1632
1633impl CtxValue {
1634 pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1635 Ok(CtxValue(Value::vec_pack(
1636 ty,
1637 values.into_iter().map(|v| v.0).collect(),
1638 )?))
1639 }
1640
1641 pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1642 self.0.coin_ref_value()
1643 }
1644
1645 pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1646 self.0.coin_ref_subtract_balance(amount)
1647 }
1648
1649 pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1650 self.0.coin_ref_add_balance(amount)
1651 }
1652
1653 pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1654 self.0.into_upgrade_ticket()
1655 }
1656
1657 pub fn to_address(&self) -> Result<AccountAddress, ExecutionError> {
1658 self.0.copy()?.cast()
1659 }
1660
1661 pub(super) fn inner_for_tracing(&self) -> &Value {
1663 &self.0
1664 }
1665}
1666
1667fn load_object_arg<Mode: ExecutionMode>(
1668 meter: &mut GasCharger,
1669 env: &Env<Mode>,
1670 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1671 input: T::ObjectInput,
1672) -> Result<(T::InputIndex, InputObjectMetadata, Value), Mode::Error> {
1673 let id = input.arg.id();
1674 let refined_permissions = input.arg.refined_permissions;
1675 let (metadata, value) = load_object_arg_impl(
1676 meter,
1677 env,
1678 input_object_map,
1679 id,
1680 refined_permissions,
1681 input.ty,
1682 )?;
1683 Ok((input.original_input_index, metadata, value))
1684}
1685
1686fn load_object_arg_impl<Mode: ExecutionMode>(
1687 meter: &mut GasCharger,
1688 env: &Env<Mode>,
1689 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1690 id: ObjectID,
1691 refined_permissions: ObjectPermissions,
1692 ty: T::Type,
1693) -> Result<(InputObjectMetadata, Value), Mode::Error> {
1694 let obj = env.read_object(&id)?;
1695 let owner = obj.owner.clone();
1696 let version = obj.version();
1697 let object_metadata = InputObjectMetadata {
1698 newly_created: false,
1699 id,
1700 refined_permissions,
1701 owner: owner.clone(),
1702 version,
1703 type_: ty.clone(),
1704 };
1705 let sui_types::object::ObjectInner {
1706 data: sui_types::object::Data::Move(move_obj),
1707 ..
1708 } = obj.as_inner()
1709 else {
1710 invariant_violation!("Expected a Move object");
1711 };
1712 assert_expected_move_object_type(&object_metadata.type_, move_obj.type_())?;
1713 let contained_uids = {
1714 let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1715 get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1716 make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1717 })?
1718 };
1719 input_object_map.insert(
1720 id,
1721 object_runtime::InputObject {
1722 contained_uids,
1723 version,
1724 owner,
1725 },
1726 );
1727
1728 let v = Value::deserialize(env, move_obj.contents(), ty)?;
1729 charge_gas_!(meter, env, charge_copy_loc, &v)?;
1730 charge_gas_!(meter, env, charge_store_loc, &v)?;
1731 Ok((object_metadata, v))
1732}
1733
1734fn load_withdrawal_arg<Mode: ExecutionMode>(
1735 meter: &mut GasCharger,
1736 env: &Env<Mode>,
1737 withdrawal: &T::WithdrawalInput,
1738) -> Result<Value, Mode::Error> {
1739 let T::WithdrawalInput {
1740 original_input_index: _,
1741 ty: _,
1742 source,
1743 amount,
1744 } = withdrawal;
1745 let loaded = match source {
1746 T::WithdrawalSource::Direct { owner } => {
1747 Value::funds_accumulator_withdrawal(*owner, *amount)
1748 }
1749 T::WithdrawalSource::Allowance { funder, id } => {
1750 Value::allowance_withdrawal(*id, *funder, *amount, false)
1752 }
1753 };
1754 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1755 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1756 Ok(loaded)
1757}
1758
1759fn load_pure_value<Mode: ExecutionMode>(
1760 meter: &mut GasCharger,
1761 env: &Env<Mode>,
1762 bytes: &[u8],
1763 metadata: &T::PureInput,
1764) -> Result<Value, Mode::Error> {
1765 let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1766 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1768 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1769 Ok(loaded)
1770}
1771
1772fn load_receiving_value<Mode: ExecutionMode>(
1773 meter: &mut GasCharger,
1774 env: &Env<Mode>,
1775 metadata: &T::ReceivingInput,
1776) -> Result<Value, Mode::Error> {
1777 let (id, version, _) = metadata.object_ref;
1778 let loaded = Value::receiving(id, version);
1779 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1780 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1781 Ok(loaded)
1782}
1783
1784fn copy_value<Mode: ExecutionMode>(
1785 meter: &mut GasCharger,
1786 env: &Env<Mode>,
1787 value: &Value,
1788) -> Result<Value, Mode::Error> {
1789 charge_gas_!(meter, env, charge_copy_loc, value)?;
1790 charge_gas_!(meter, env, charge_pop, value)?;
1791 Ok(value.copy()?)
1792}
1793
1794fn refund_max_gas_budget<OType>(
1798 writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1799 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1800 gas_charger: &mut GasCharger,
1801 gas_id: ObjectID,
1802 gas_coin_transfer: Option<&GasCoinTransfer>,
1803) -> Result<(), ExecutionError> {
1804 match gas_coin_transfer {
1805 Some(GasCoinTransfer::SendFunds { recipient, .. }) => {
1806 assert_invariant!(
1809 !writes.contains_key(&gas_id),
1810 "Gas coin should not be in writes if it was used with send_funds"
1811 );
1812 balance_change_accumulator_event(
1813 accumulator_events,
1814 *recipient,
1815 checked_as!(gas_charger.gas_budget(), i64)?,
1816 )?;
1817 }
1818 Some(GasCoinTransfer::TransferObjects) | None => {
1819 let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1820 invariant_violation!("Gas object cannot be wrapped or destroyed")
1821 };
1822 let value = std::mem::replace(value_ref, VMValue::u8(0));
1824 let mut locals = Locals::new([Some(value.into())])?;
1825 let mut local = locals.local(0)?;
1826 let coin_value = local.borrow()?.coin_ref_value()?;
1827 let additional = gas_charger.gas_budget();
1828 if coin_value.checked_add(additional).is_none() {
1829 return Err(ExecutionError::new_with_source(
1830 ExecutionErrorKind::CoinBalanceOverflow,
1831 "Gas coin too large after returning the max gas budget",
1832 ));
1833 };
1834 local.borrow()?.coin_ref_add_balance(additional)?;
1835 *value_ref = local.move_()?.into();
1837 }
1838 };
1839 Ok(())
1840}
1841
1842fn finish_gas_coin<OType>(
1848 gas_charger: &mut GasCharger,
1849 writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1850 created_object_ids: &mut IndexSet<ObjectID>,
1851 deleted_object_ids: &IndexSet<ObjectID>,
1852 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1853 gas_id: ObjectID,
1854 gas_payment: GasPayment,
1855 gas_coin_transfer: Option<GasCoinTransfer>,
1856) -> Result<(), ExecutionError> {
1857 refund_max_gas_budget(
1859 writes,
1860 accumulator_events,
1861 gas_charger,
1862 gas_id,
1863 gas_coin_transfer.as_ref(),
1864 )?;
1865
1866 match &gas_coin_transfer {
1872 Some(GasCoinTransfer::SendFunds { recipient, .. }) => {
1873 gas_charger.override_gas_charge_location(PaymentLocation::AddressBalance(
1874 (*recipient).into(),
1875 ))?;
1876 }
1877 Some(GasCoinTransfer::TransferObjects) => {
1878 gas_charger.override_gas_charge_location(PaymentLocation::Coin(gas_id))?;
1879 }
1880 None => (),
1881 }
1882
1883 let address = match gas_payment.location {
1885 PaymentLocation::Coin(_) => {
1886 assert_invariant!(
1888 !matches!(gas_coin_transfer, Some(GasCoinTransfer::SendFunds { .. }))
1889 || deleted_object_ids.contains(&gas_id),
1890 "send_funds transfer implies the coin should be deleted"
1891 );
1892 return Ok(());
1893 }
1894 PaymentLocation::AddressBalance(address) => address,
1895 };
1896
1897 let net_balance_change = if let Some(gas_coin_transfer) = gas_coin_transfer {
1898 match gas_coin_transfer {
1900 GasCoinTransfer::TransferObjects => {
1901 assert_invariant!(
1902 created_object_ids.contains(&gas_id),
1903 "ephemeral coin should be newly created"
1904 );
1905 assert_invariant!(
1906 !deleted_object_ids.contains(&gas_id),
1907 "ephemeral coin should not be deleted if transferred as an object"
1908 );
1909 assert_invariant!(
1910 writes.contains_key(&gas_id),
1911 "ephemeral coin should be in writes if transferred as an object"
1912 );
1913 }
1914 GasCoinTransfer::SendFunds { .. } => {
1915 assert_invariant!(
1916 !created_object_ids.contains(&gas_id),
1917 "ephemeral coin should not be newly created if transferred with send_funds"
1918 );
1919 assert_invariant!(
1920 !deleted_object_ids.contains(&gas_id),
1921 "ephemeral coin should not be deleted if transferred with send_funds"
1922 );
1923 assert_invariant!(
1924 !writes.contains_key(&gas_id),
1925 "ephemeral coin should not be in writes if transferred with send_funds"
1926 );
1927 }
1928 }
1929
1930 let Some(net_balance_change) = gas_payment
1935 .amount
1936 .try_into()
1937 .ok()
1938 .and_then(|i: i64| i.checked_neg())
1939 else {
1940 invariant_violation!("Gas payment amount cannot be represented as i64")
1941 };
1942 net_balance_change
1943 } else {
1944 let was_created = created_object_ids.shift_remove(&gas_id);
1948 assert_invariant!(was_created, "ephemeral coin should be newly created");
1949 let Some((_owner, _ty, value)) = writes.shift_remove(&gas_id) else {
1950 invariant_violation!("checked above that the gas coin was present")
1951 };
1952 let (_id, remaining_balance) = Value::from(value).unpack_coin()?;
1953 let Some(net_balance_change): Option<i64> = (remaining_balance as i128)
1957 .checked_sub(gas_payment.amount as i128)
1958 .and_then(|i| i.try_into().ok())
1959 else {
1960 invariant_violation!("Remaining balance could not be represented as i64")
1961 };
1962 net_balance_change
1963 };
1964 balance_change_accumulator_event(accumulator_events, address.into(), net_balance_change)?;
1965 Ok(())
1966}
1967
1968fn balance_change_accumulator_event(
1969 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1970 address: AccountAddress,
1971 balance_change: i64,
1972) -> Result<(), ExecutionError> {
1973 if balance_change == 0 {
1974 return Ok(());
1975 }
1976 let balance_type = Balance::type_tag(sui_types::gas_coin::GAS::type_tag());
1977 let Some(accumulator_id) =
1978 accumulator_root::AccumulatorValue::get_field_id(address.into(), &balance_type).ok()
1979 else {
1980 invariant_violation!("Failed to compute accumulator field id")
1981 };
1982 let (action, value) = if balance_change < 0 {
1983 (
1984 MoveAccumulatorAction::Split,
1985 MoveAccumulatorValue::U64(balance_change.unsigned_abs()),
1986 )
1987 } else {
1988 (
1989 MoveAccumulatorAction::Merge,
1990 MoveAccumulatorValue::U64(balance_change as u64),
1991 )
1992 };
1993 accumulator_events.push(MoveAccumulatorEvent {
1994 accumulator_id: *accumulator_id.inner(),
1995 action,
1996 target_addr: address,
1997 target_ty: balance_type,
1998 value,
1999 });
2000 Ok(())
2001}
2002
2003unsafe fn create_written_object<Mode: ExecutionMode>(
2009 env: &Env<Mode>,
2010 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
2011 id: ObjectID,
2012 type_: Type,
2013 has_public_transfer: bool,
2014 contents: Vec<u8>,
2015) -> Result<MoveObject, ExecutionError> {
2016 debug_assert_eq!(
2017 id,
2018 MoveObject::id_opt(&contents).expect("object contents should start with an id")
2019 );
2020 let old_obj_ver = objects_modified_at
2021 .get(&id)
2022 .map(|obj: &LoadedRuntimeObject| obj.version);
2023
2024 let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
2025 invariant_violation!("unable to generate type tag from type")
2026 };
2027
2028 let struct_tag = match type_tag {
2029 TypeTag::Struct(inner) => *inner,
2030 _ => invariant_violation!("Non struct type for object"),
2031 };
2032 unsafe {
2033 MoveObject::new_from_execution(
2034 struct_tag.into(),
2035 has_public_transfer,
2036 old_obj_ver.unwrap_or_default(),
2037 contents,
2038 env.protocol_config,
2039 Mode::packages_are_predefined(),
2040 )
2041 }
2042}
2043
2044pub fn subst_signature(
2046 signature: LoadedFunctionInformation,
2047 type_arguments: &[VMType],
2048) -> VMResult<LoadedFunctionInformation> {
2049 let LoadedFunctionInformation {
2050 parameters,
2051 return_,
2052 is_entry,
2053 is_native,
2054 visibility,
2055 index,
2056 instruction_count,
2057 } = signature;
2058 let parameters = parameters
2059 .into_iter()
2060 .map(|ty| ty.subst(type_arguments))
2061 .collect::<PartialVMResult<Vec<_>>>()
2062 .map_err(|err| err.finish(Location::Undefined))?;
2063 let return_ = return_
2064 .into_iter()
2065 .map(|ty| ty.subst(type_arguments))
2066 .collect::<PartialVMResult<Vec<_>>>()
2067 .map_err(|err| err.finish(Location::Undefined))?;
2068 Ok(LoadedFunctionInformation {
2069 parameters,
2070 return_,
2071 is_entry,
2072 is_native,
2073 visibility,
2074 index,
2075 instruction_count,
2076 })
2077}
2078
2079pub enum EitherError<E: ExecutionErrorTrait = ExecutionError> {
2080 CommandArgument(CommandArgumentError),
2081 Execution(E),
2082}
2083
2084impl<E: ExecutionErrorTrait> From<ExecutionError> for EitherError<E> {
2085 fn from(e: ExecutionError) -> Self {
2086 EitherError::Execution(e.into())
2087 }
2088}
2089
2090impl<E: ExecutionErrorTrait> From<CommandArgumentError> for EitherError<E> {
2091 fn from(e: CommandArgumentError) -> Self {
2092 EitherError::CommandArgument(e)
2093 }
2094}
2095
2096impl<E: ExecutionErrorTrait> EitherError<E> {
2097 pub fn into_execution_error(self, command_index: usize) -> E {
2098 match self {
2099 EitherError::CommandArgument(e) => command_argument_error(e, command_index).into(),
2100 EitherError::Execution(e) => e,
2101 }
2102 }
2103}
2104
2105#[derive(Debug)]
2113pub enum PrimitiveArgumentLayout {
2114 Option(Box<PrimitiveArgumentLayout>),
2116 Vector(Box<PrimitiveArgumentLayout>),
2118 Ascii,
2120 UTF8,
2122 Bool,
2124 U8,
2125 U16,
2126 U32,
2127 U64,
2128 U128,
2129 U256,
2130 Address,
2131}
2132
2133impl PrimitiveArgumentLayout {
2134 pub fn bcs_only(&self) -> bool {
2138 match self {
2139 PrimitiveArgumentLayout::Option(_)
2141 | PrimitiveArgumentLayout::Ascii
2142 | PrimitiveArgumentLayout::UTF8 => false,
2143 PrimitiveArgumentLayout::Bool
2145 | PrimitiveArgumentLayout::U8
2146 | PrimitiveArgumentLayout::U16
2147 | PrimitiveArgumentLayout::U32
2148 | PrimitiveArgumentLayout::U64
2149 | PrimitiveArgumentLayout::U128
2150 | PrimitiveArgumentLayout::U256
2151 | PrimitiveArgumentLayout::Address => true,
2152 PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
2154 }
2155 }
2156}
2157
2158pub fn bcs_argument_validate(
2162 bytes: &[u8],
2163 idx: u16,
2164 layout: PrimitiveArgumentLayout,
2165) -> Result<(), ExecutionError> {
2166 bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
2167 ExecutionError::new_with_source(
2168 ExecutionErrorKind::command_argument_error(CommandArgumentError::InvalidBCSBytes, idx),
2169 format!("Function expects {layout} but provided argument's value does not match",),
2170 )
2171 })
2172}
2173
2174impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
2175 type Value = ();
2176 fn deserialize<D: serde::de::Deserializer<'d>>(
2177 self,
2178 deserializer: D,
2179 ) -> Result<Self::Value, D::Error> {
2180 use serde::de::Error;
2181 match self {
2182 PrimitiveArgumentLayout::Ascii => {
2183 let s: &str = serde::Deserialize::deserialize(deserializer)?;
2184 if !s.is_ascii() {
2185 Err(D::Error::custom("not an ascii string"))
2186 } else {
2187 Ok(())
2188 }
2189 }
2190 PrimitiveArgumentLayout::UTF8 => {
2191 deserializer.deserialize_string(serde::de::IgnoredAny)?;
2192 Ok(())
2193 }
2194 PrimitiveArgumentLayout::Option(layout) => {
2195 deserializer.deserialize_option(OptionElementVisitor(layout))
2196 }
2197 PrimitiveArgumentLayout::Vector(layout) => {
2198 deserializer.deserialize_seq(VectorElementVisitor(layout))
2199 }
2200 PrimitiveArgumentLayout::Bool => {
2203 deserializer.deserialize_bool(serde::de::IgnoredAny)?;
2204 Ok(())
2205 }
2206 PrimitiveArgumentLayout::U8 => {
2207 deserializer.deserialize_u8(serde::de::IgnoredAny)?;
2208 Ok(())
2209 }
2210 PrimitiveArgumentLayout::U16 => {
2211 deserializer.deserialize_u16(serde::de::IgnoredAny)?;
2212 Ok(())
2213 }
2214 PrimitiveArgumentLayout::U32 => {
2215 deserializer.deserialize_u32(serde::de::IgnoredAny)?;
2216 Ok(())
2217 }
2218 PrimitiveArgumentLayout::U64 => {
2219 deserializer.deserialize_u64(serde::de::IgnoredAny)?;
2220 Ok(())
2221 }
2222 PrimitiveArgumentLayout::U128 => {
2223 deserializer.deserialize_u128(serde::de::IgnoredAny)?;
2224 Ok(())
2225 }
2226 PrimitiveArgumentLayout::U256 => {
2227 U256::deserialize(deserializer)?;
2228 Ok(())
2229 }
2230 PrimitiveArgumentLayout::Address => {
2231 SuiAddress::deserialize(deserializer)?;
2232 Ok(())
2233 }
2234 }
2235 }
2236}
2237
2238struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2239
2240impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
2241 type Value = ();
2242
2243 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2244 formatter.write_str("Vector")
2245 }
2246
2247 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
2248 where
2249 A: serde::de::SeqAccess<'d>,
2250 {
2251 while seq.next_element_seed(self.0)?.is_some() {}
2252 Ok(())
2253 }
2254}
2255
2256struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2257
2258impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
2259 type Value = ();
2260
2261 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2262 formatter.write_str("Option")
2263 }
2264
2265 fn visit_none<E>(self) -> Result<Self::Value, E>
2266 where
2267 E: serde::de::Error,
2268 {
2269 Ok(())
2270 }
2271
2272 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
2273 where
2274 D: serde::Deserializer<'d>,
2275 {
2276 self.0.deserialize(deserializer)
2277 }
2278}
2279
2280impl fmt::Display for PrimitiveArgumentLayout {
2281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2282 match self {
2283 PrimitiveArgumentLayout::Vector(inner) => {
2284 write!(f, "vector<{inner}>")
2285 }
2286 PrimitiveArgumentLayout::Option(inner) => {
2287 write!(f, "std::option::Option<{inner}>")
2288 }
2289 PrimitiveArgumentLayout::Ascii => {
2290 write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
2291 }
2292 PrimitiveArgumentLayout::UTF8 => {
2293 write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
2294 }
2295 PrimitiveArgumentLayout::Bool => write!(f, "bool"),
2296 PrimitiveArgumentLayout::U8 => write!(f, "u8"),
2297 PrimitiveArgumentLayout::U16 => write!(f, "u16"),
2298 PrimitiveArgumentLayout::U32 => write!(f, "u32"),
2299 PrimitiveArgumentLayout::U64 => write!(f, "u64"),
2300 PrimitiveArgumentLayout::U128 => write!(f, "u128"),
2301 PrimitiveArgumentLayout::U256 => write!(f, "u256"),
2302 PrimitiveArgumentLayout::Address => write!(f, "address"),
2303 }
2304 }
2305}
2306
2307pub fn finish(
2308 protocol_config: &ProtocolConfig,
2309 state_view: &dyn ExecutionState,
2310 gas_charger: &mut GasCharger,
2311 tx_context: &TxContext,
2312 by_value_shared_objects: &BTreeSet<ObjectID>,
2313 consensus_owner_objects: &BTreeMap<ObjectID, Owner>,
2314 loaded_runtime_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
2315 written_objects: BTreeMap<ObjectID, Object>,
2316 created_object_ids: IndexSet<ObjectID>,
2317 deleted_object_ids: IndexSet<ObjectID>,
2318 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
2319 accumulator_events: Vec<MoveAccumulatorEvent>,
2320 settlement_input_sui: u64,
2321 settlement_output_sui: u64,
2322) -> Result<ExecutionResults, ExecutionError> {
2323 for id in by_value_shared_objects {
2328 if let Some(obj) = written_objects.get(id) {
2331 if !obj.is_shared() {
2332 return Err(ExecutionError::new(
2333 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2334 Some(
2335 format!(
2336 "Shared object operation on {} not allowed: \
2337 cannot be frozen, transferred, or wrapped",
2338 id
2339 )
2340 .into(),
2341 ),
2342 ));
2343 }
2344 } else {
2345 if !deleted_object_ids.contains(id) {
2348 return Err(ExecutionError::new(
2349 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2350 Some(
2351 format!(
2352 "Shared object operation on {} not allowed: \
2353 shared objects used by value must be re-shared if not deleted",
2354 id
2355 )
2356 .into(),
2357 ),
2358 ));
2359 }
2360 }
2361 }
2362
2363 for (id, original_owner) in consensus_owner_objects {
2365 let Owner::ConsensusAddressOwner { owner, .. } = original_owner else {
2366 panic!(
2367 "verified before adding to `consensus_owner_objects` that these are ConsensusAddressOwner"
2368 );
2369 };
2370 if tx_context.sender() != *owner {
2373 debug_fatal!(
2374 "transaction with a singly owned input object where the tx sender is not the owner should never be executed"
2375 );
2376 return Err(ExecutionError::new(
2377 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2378 Some(
2379 format!(
2380 "Shared object operation on {} not allowed: \
2381 transaction with singly owned input object must be sent by the owner",
2382 id
2383 )
2384 .into(),
2385 ),
2386 ));
2387 }
2388 }
2396
2397 let user_events: Vec<Event> = user_events
2398 .into_iter()
2399 .map(|(module_id, tag, contents)| {
2400 Event::new(
2401 module_id.address(),
2402 module_id.name(),
2403 tx_context.sender(),
2404 tag,
2405 contents,
2406 )
2407 })
2408 .collect();
2409
2410 let mut receiving_funds_type_and_owners = BTreeMap::new();
2411 let accumulator_events = accumulator_events
2412 .into_iter()
2413 .map(|accum_event| {
2414 if let Some(ty) = Balance::maybe_get_balance_type_param(&accum_event.target_ty) {
2415 receiving_funds_type_and_owners
2416 .entry(ty)
2417 .or_insert_with(BTreeSet::new)
2418 .insert(accum_event.target_addr.into());
2419 }
2420 let value = match accum_event.value {
2421 MoveAccumulatorValue::U64(amount) => AccumulatorValue::Integer(amount),
2422 MoveAccumulatorValue::EventRef(event_idx) => {
2423 let Some(event) = user_events.get(checked_as!(event_idx, usize)?) else {
2424 invariant_violation!(
2425 "Could not find authenticated event at index {}",
2426 event_idx
2427 );
2428 };
2429 let digest = event.digest();
2430 AccumulatorValue::EventDigest(nonempty![(event_idx, digest)])
2431 }
2432 };
2433
2434 let address =
2435 AccumulatorAddress::new(accum_event.target_addr.into(), accum_event.target_ty);
2436
2437 let write = AccumulatorWriteV1 {
2438 address,
2439 operation: accum_event.action.into_sui_accumulator_action(),
2440 value,
2441 };
2442
2443 Ok(AccumulatorEvent::new(
2444 AccumulatorObjId::new_unchecked(accum_event.accumulator_id),
2445 write,
2446 ))
2447 })
2448 .collect::<Result<Vec<_>, ExecutionError>>()?;
2449
2450 for object in written_objects.values() {
2452 let coin_type = object.type_().and_then(|ty| ty.coin_type_maybe());
2453 let owner = object.owner.get_owner_address();
2454 if let (Some(ty), Ok(owner)) = (coin_type, owner) {
2455 receiving_funds_type_and_owners
2456 .entry(ty)
2457 .or_insert_with(BTreeSet::new)
2458 .insert(owner);
2459 }
2460 }
2461 let DenyListResult {
2462 result,
2463 num_non_gas_coin_owners,
2464 } = state_view.check_coin_deny_list(receiving_funds_type_and_owners);
2465 gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
2466 result?;
2467
2468 let created_object_ids: BTreeSet<ObjectID> = created_object_ids.into_iter().collect();
2469 let deleted_object_ids: BTreeSet<ObjectID> = deleted_object_ids.into_iter().collect();
2470 let modified_objects: BTreeSet<ObjectID> = loaded_runtime_objects
2471 .into_iter()
2472 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
2473 .collect();
2474
2475 assert_invariant!(
2476 created_object_ids.is_disjoint(&deleted_object_ids),
2477 "Created and deleted object sets should be disjoint"
2478 );
2479 assert_invariant!(
2480 modified_objects.is_disjoint(&created_object_ids),
2481 "Modified and created object sets should be disjoint"
2482 );
2483 assert_invariant!(
2484 written_objects
2485 .keys()
2486 .all(|id| !deleted_object_ids.contains(id)),
2487 "Written objects should not be deleted"
2488 );
2489 Ok(ExecutionResults::V2(ExecutionResultsV2 {
2490 written_objects,
2491 modified_objects,
2492 created_object_ids,
2493 deleted_object_ids,
2494 user_events,
2495 accumulator_events,
2496 settlement_input_sui,
2497 settlement_output_sui,
2498 }))
2499}
2500
2501pub fn fetch_package(
2502 state_view: &impl BackingPackageStore,
2503 package_id: &ObjectID,
2504) -> Result<PackageObject, ExecutionError> {
2505 let mut fetched_packages = fetch_packages(state_view, vec![package_id])?;
2506 assert_invariant!(
2507 fetched_packages.len() == 1,
2508 "Number of fetched packages must match the number of package object IDs if successful."
2509 );
2510 match fetched_packages.pop() {
2511 Some(pkg) => Ok(pkg),
2512 None => invariant_violation!(
2513 "We should always fetch a package for each object or return a dependency error."
2514 ),
2515 }
2516}
2517
2518pub fn fetch_packages<'ctx, 'state>(
2519 state_view: &'state impl BackingPackageStore,
2520 package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
2521) -> Result<Vec<PackageObject>, ExecutionError> {
2522 let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
2523 match get_package_objects(state_view, package_ids) {
2524 Err(e) => Err(ExecutionError::new_with_source(
2525 ExecutionErrorKind::PublishUpgradeMissingDependency,
2526 e,
2527 )),
2528 Ok(Err(missing_deps)) => {
2529 let msg = format!(
2530 "Missing dependencies: {}",
2531 missing_deps
2532 .into_iter()
2533 .map(|dep| format!("{}", dep))
2534 .collect::<Vec<_>>()
2535 .join(", ")
2536 );
2537 Err(ExecutionError::new_with_source(
2538 ExecutionErrorKind::PublishUpgradeMissingDependency,
2539 msg,
2540 ))
2541 }
2542 Ok(Ok(pkgs)) => Ok(pkgs),
2543 }
2544}
2545
2546pub fn check_compatibility(
2547 protocol_config: &ProtocolConfig,
2548 existing_package: &MovePackage,
2549 upgrading_modules: &[CompiledModule],
2550 policy: u8,
2551) -> Result<(), ExecutionError> {
2552 let Ok(policy) = UpgradePolicy::try_from(policy) else {
2554 return Err(ExecutionError::from_kind(
2555 ExecutionErrorKind::PackageUpgradeError {
2556 upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
2557 },
2558 ));
2559 };
2560
2561 let pool = &mut normalized::RcPool::new();
2562 let binary_config = protocol_config.binary_config(None);
2563 let Ok(current_normalized) =
2564 existing_package.normalize(pool, &binary_config, true)
2565 else {
2566 invariant_violation!("Tried to normalize modules in existing package but failed")
2567 };
2568
2569 let existing_modules_len = current_normalized.len();
2570 let upgrading_modules_len = upgrading_modules.len();
2571 let disallow_new_modules = policy as u8 == UpgradePolicy::DEP_ONLY;
2572
2573 if disallow_new_modules && existing_modules_len != upgrading_modules_len {
2574 return Err(ExecutionError::new_with_source(
2575 ExecutionErrorKind::PackageUpgradeError {
2576 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2577 },
2578 format!(
2579 "Existing package has {existing_modules_len} modules, but new package has \
2580 {upgrading_modules_len}. Adding or removing a module to a deps only package is not allowed."
2581 ),
2582 ));
2583 }
2584
2585 let mut new_normalized = normalize_deserialized_modules(
2586 pool,
2587 upgrading_modules.iter(),
2588 true,
2589 );
2590 for (name, cur_module) in current_normalized {
2591 let Some(new_module) = new_normalized.remove(&name) else {
2592 return Err(ExecutionError::new_with_source(
2593 ExecutionErrorKind::PackageUpgradeError {
2594 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2595 },
2596 format!("Existing module {name} not found in next version of package"),
2597 ));
2598 };
2599
2600 check_module_compatibility(&policy, &cur_module, &new_module)?;
2601 }
2602
2603 debug_assert!(!disallow_new_modules || new_normalized.is_empty());
2605
2606 Ok(())
2607}
2608
2609fn check_module_compatibility(
2610 policy: &UpgradePolicy,
2611 cur_module: &move_binary_format::compatibility::Module,
2612 new_module: &move_binary_format::compatibility::Module,
2613) -> Result<(), ExecutionError> {
2614 match policy {
2615 UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
2616 UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
2617 UpgradePolicy::Compatible => {
2618 let compatibility = Compatibility::upgrade_check();
2619
2620 compatibility.check(cur_module, new_module)
2621 }
2622 }
2623 .map_err(|e| {
2624 ExecutionError::new_with_source(
2625 ExecutionErrorKind::PackageUpgradeError {
2626 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2627 },
2628 e,
2629 )
2630 })
2631}
2632
2633fn assert_expected_move_object_type(
2636 actual: &Type,
2637 expected: &MoveObjectType,
2638) -> Result<(), ExecutionError> {
2639 let Type::Datatype(actual) = actual else {
2640 invariant_violation!("Expected a datatype for a Move object");
2641 };
2642 let (a, m, n) = actual.qualified_ident();
2643 assert_invariant!(
2644 a == &expected.address(),
2645 "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
2646 );
2647 assert_invariant!(
2648 m == expected.module(),
2649 "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
2650 );
2651 assert_invariant!(
2652 n == expected.name(),
2653 "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
2654 );
2655 let actual_type_arguments = &actual.type_arguments;
2656 let expected_type_arguments = expected.type_params();
2657 assert_invariant!(
2658 actual_type_arguments.len() == expected_type_arguments.len(),
2659 "Actual type arg length does not match expected. \
2660 actual: {actual:?} vs expected: {expected:?}",
2661 );
2662 for (actual_ty, expected_ty) in actual_type_arguments
2663 .iter()
2664 .zip_debug_eq(&expected_type_arguments)
2665 {
2666 assert_expected_type(actual_ty, expected_ty)?;
2667 }
2668 Ok(())
2669}
2670
2671fn assert_expected_type(actual: &Type, expected: &TypeTag) -> Result<(), ExecutionError> {
2674 match (actual, expected) {
2675 (Type::Bool, TypeTag::Bool)
2676 | (Type::U8, TypeTag::U8)
2677 | (Type::U16, TypeTag::U16)
2678 | (Type::U32, TypeTag::U32)
2679 | (Type::U64, TypeTag::U64)
2680 | (Type::U128, TypeTag::U128)
2681 | (Type::U256, TypeTag::U256)
2682 | (Type::Address, TypeTag::Address)
2683 | (Type::Signer, TypeTag::Signer) => Ok(()),
2684 (Type::Vector(inner_actual), TypeTag::Vector(inner_expected)) => {
2685 assert_expected_type(&inner_actual.element_type, inner_expected)
2686 }
2687 (Type::Datatype(actual_dt), TypeTag::Struct(expected_st)) => {
2688 assert_expected_data_type(actual_dt, expected_st)
2689 }
2690 _ => invariant_violation!(
2691 "Type mismatch between actual: {actual:?} and expected: {expected:?}"
2692 ),
2693 }
2694}
2695fn assert_expected_data_type(
2698 actual: &Datatype,
2699 expected: &StructTag,
2700) -> Result<(), ExecutionError> {
2701 let (a, m, n) = actual.qualified_ident();
2702 assert_invariant!(
2703 a == &expected.address,
2704 "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
2705 );
2706 assert_invariant!(
2707 m == expected.module.as_ident_str(),
2708 "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
2709 );
2710 assert_invariant!(
2711 n == expected.name.as_ident_str(),
2712 "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
2713 );
2714 let actual_type_arguments = &actual.type_arguments;
2715 let expected_type_arguments = &expected.type_params;
2716 assert_invariant!(
2717 actual_type_arguments.len() == expected_type_arguments.len(),
2718 "Actual type arg length does not match expected. \
2719 actual: {actual:?} vs expected: {expected:?}",
2720 );
2721 for (actual_ty, expected_ty) in actual_type_arguments
2722 .iter()
2723 .zip_debug_eq(expected_type_arguments)
2724 {
2725 assert_expected_type(actual_ty, expected_ty)?;
2726 }
2727 Ok(())
2728}