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