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