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::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, 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 mutability: ObjectMutability,
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 mutability: ObjectMutability::Mutable,
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 ObjectMutability::Mutable,
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 mutability,
524 owner,
525 version,
526 type_,
527 } = metadata;
528 match mutability {
529 ObjectMutability::Immutable => continue,
530 ObjectMutability::NonExclusiveWrite | ObjectMutability::Mutable => (),
534 }
535
536 if newly_created {
537 created_input_object_ids.insert(id);
538 } else {
539 loaded_runtime_objects.insert(
540 id,
541 LoadedRuntimeObject {
542 version,
543 is_modified: true,
544 },
545 );
546 }
547 if let Some(object) = value_opt {
548 self.transfer_object_(
549 owner,
550 type_,
551 CtxValue(object),
552 true,
553 )?;
554 } else if owner.is_shared() {
555 by_value_shared_objects.insert(id);
556 } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
557 consensus_owner_objects.insert(id, owner.clone());
558 }
559 }
560
561 let Self {
562 env,
563 native_extensions,
564 tx_context,
565 gas_charger,
566 user_events,
567 ..
568 } = self;
569 let ref_context: &RefCell<TxContext> = &tx_context;
570 let tx_context: &TxContext = &ref_context.borrow();
571 let tx_digest = ref_context.borrow().digest();
572
573 let object_runtime: ObjectRuntime = native_extensions
574 .try_borrow_mut()
575 .map_err(|_| {
576 make_invariant_violation!(
577 "Should be able to borrow object runtime native extension at the end of execution"
578 )
579 })?
580 .remove()
581 .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
582
583 let RuntimeResults {
584 mut writes,
585 user_events: remaining_events,
586 loaded_child_objects,
587 mut created_object_ids,
588 deleted_object_ids,
589 mut accumulator_events,
590 settlement_input_sui,
591 settlement_output_sui,
592 } = object_runtime.finish()?;
593 assert_invariant!(
594 loaded_runtime_objects
595 .keys()
596 .all(|id| !created_object_ids.contains(id)),
597 "Loaded input objects should not be in the created objects set"
598 );
599
600 assert_invariant!(
601 remaining_events.is_empty(),
602 "Events should be taken after every Move call"
603 );
604 if let Some(gas_id) = gas_id_opt {
606 assert_invariant!(
608 !deleted_object_ids.contains(&gas_id)
609 || gas_coin_transfer.is_some_and(|destination| matches!(
610 destination,
611 GasCoinTransfer::SendFunds { .. }
612 )),
613 "Gas coin should not be deleted"
614 );
615 let Some(gas_payment_location) = gas_payment_location else {
616 invariant_violation!("Gas payment should be specified if gas ID is present");
617 };
618 finish_gas_coin(
619 gas_charger,
620 &mut writes,
621 &mut created_object_ids,
622 &deleted_object_ids,
623 &mut accumulator_events,
624 gas_id,
625 gas_payment_location,
626 gas_coin_transfer,
627 )?;
628 }
629
630 loaded_runtime_objects.extend(loaded_child_objects);
631
632 let mut written_objects = BTreeMap::new();
633
634 let (writeout_vm, ty_linkage) =
635 Self::make_writeout_vm(env, writes.values().map(|(_, ty, _)| ty.clone()))?;
636
637 for (id, (recipient, ty, value)) in writes {
638 let (ty, layout) = Self::load_type_and_layout_from_struct_for_writeout(
639 env,
640 &writeout_vm,
641 &ty_linkage,
642 ty.clone().into(),
643 )?;
644 let abilities = ty.abilities();
645 let has_public_transfer = abilities.has_store();
646 let Some(bytes) = value.typed_serialize(&layout) else {
647 invariant_violation!("Failed to serialize already deserialized Move value");
648 };
649 let move_object = unsafe {
651 create_written_object::<Mode>(
652 env,
653 &loaded_runtime_objects,
654 id,
655 ty,
656 has_public_transfer,
657 bytes,
658 )?
659 };
660 let object = Object::new_move(move_object, recipient, tx_digest);
661 written_objects.insert(id, object);
662 }
663
664 for package in self
665 .env
666 .linkable_store
667 .package_store
668 .to_new_packages()
669 .into_iter()
670 {
671 let package_obj = Object::new_from_package(package, tx_digest);
672 let id = package_obj.id();
673 created_object_ids.insert(id);
674 written_objects.insert(id, package_obj);
675 }
676
677 Ok(execution::context::finish(
678 env.protocol_config,
679 env.state_view,
680 gas_charger,
681 tx_context,
682 &by_value_shared_objects,
683 &consensus_owner_objects,
684 loaded_runtime_objects,
685 written_objects,
686 created_object_ids,
687 deleted_object_ids,
688 user_events,
689 accumulator_events,
690 settlement_input_sui,
691 settlement_output_sui,
692 )?)
693 }
694
695 pub fn take_user_events(
696 &mut self,
697 vm: &MoveVM<'_>,
698 version_mid: ModuleId,
699 function_def_idx: FunctionDefinitionIndex,
700 instr_length: u16,
701 linkage: &ExecutableLinkage,
702 ) -> Result<(), Mode::Error> {
703 let events = object_runtime_mut!(self)?.take_user_events();
704 let Some(num_events) = self.user_events.len().checked_add(events.len()) else {
705 invariant_violation!("usize overflow, too many events emitted")
706 };
707 let max_events = self.env.protocol_config.max_num_event_emit();
708 if num_events as u64 > max_events {
709 let err = max_event_error(max_events)
710 .at_code_offset(function_def_idx, instr_length)
711 .finish(Location::Module(version_mid.clone()));
712 return Err(self.env.convert_linked_vm_error(err, linkage));
713 }
714 let new_events = events
715 .into_iter()
716 .map(|(tag, value)| {
717 let type_tag = TypeTag::Struct(Box::new(tag));
718 let layout = vm
719 .runtime_type_layout(&type_tag)
720 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
721 let Some(bytes) = value.typed_serialize(&layout) else {
722 invariant_violation!("Failed to serialize Move event");
723 };
724 let TypeTag::Struct(tag) = type_tag else {
725 unreachable!()
726 };
727 Ok((version_mid.clone(), *tag, bytes))
728 })
729 .collect::<Result<Vec<_>, Mode::Error>>()?;
730 self.user_events.extend(new_events);
731 Ok(())
732 }
733
734 fn make_writeout_vm<I>(
743 env: &Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
744 writes: I,
745 ) -> Result<(MoveVM<'extension>, ExecutableLinkage), Mode::Error>
746 where
747 I: IntoIterator<Item = MoveObjectType>,
748 {
749 let tys_addrs = writes
750 .into_iter()
751 .flat_map(|ty| StructTag::from(ty).all_addresses())
752 .map(ObjectID::from)
753 .collect::<BTreeSet<_>>();
754
755 let ty_linkage = ExecutableLinkage::type_linkage::<_, Mode::Error>(
756 env.linkage_analysis.config().clone(),
757 &tys_addrs,
758 env.linkable_store,
759 )?;
760 env.vm
761 .make_vm(
762 &env.linkable_store.package_store,
763 ty_linkage.linkage_context::<Mode::Error>()?,
764 )
765 .map_err(|e| env.convert_linked_vm_error(e, &ty_linkage))
766 .map(|vm| (vm, ty_linkage))
767 }
768
769 fn load_type_and_layout_from_struct_for_writeout(
774 env: &Env<'pc, 'vm, 'state, 'linkage, 'extension, Mode>,
775 vm: &MoveVM,
776 linkage: &ExecutableLinkage,
777 tag: StructTag,
778 ) -> Result<(Type, move_core_types::runtime_value::MoveTypeLayout), Mode::Error> {
779 let type_tag = TypeTag::Struct(Box::new(tag));
780 let vm_type = vm
781 .load_type(&type_tag)
782 .map_err(|e| env.convert_linked_vm_error(e, linkage))?;
783 let layout = vm
784 .runtime_type_layout(&type_tag)
785 .map_err(|e| env.convert_vm_error(e))?;
786 env.adapter_type_from_vm_type(vm, &vm_type)
787 .map(|ty| (ty, layout))
788 }
789
790 fn location(&mut self, usage: UsageKind, location: T::Location) -> Result<Value, Mode::Error> {
795 let resolved = self.locations.resolve(location)?;
796 let mut local = match resolved {
797 ResolvedLocation::Local(l) => l,
798 ResolvedLocation::Pure {
799 bytes,
800 metadata,
801 mut local,
802 } => {
803 if local.is_invalid()? {
804 let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
805 local.store(v)?;
806 }
807 local
808 }
809 ResolvedLocation::Receiving {
810 metadata,
811 mut local,
812 } => {
813 if local.is_invalid()? {
814 let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
815 local.store(v)?;
816 }
817 local
818 }
819 };
820 Ok(match usage {
821 UsageKind::Move => {
822 let value = local.move_()?;
823 charge_gas_!(self.gas_charger, self.env, charge_move_loc, &value)?;
824 value
825 }
826 UsageKind::Copy => {
827 let value = local.copy()?;
828 charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
829 value
830 }
831 UsageKind::Borrow => {
832 charge_gas_!(
833 self.gas_charger,
834 self.env,
835 charge_simple_instr(SimpleInstruction::MutBorrowLoc)
836 )?;
837 local.borrow()?
838 }
839 })
840 }
841
842 fn location_usage(&mut self, usage: T::Usage) -> Result<Value, Mode::Error> {
843 match usage {
844 T::Usage::Move(location) => self.location(UsageKind::Move, location),
845 T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
846 }
847 }
848
849 fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, Mode::Error> {
850 match arg_ {
851 T::Argument__::Use(usage) => self.location_usage(usage),
852 T::Argument__::Freeze(usage) => self.location_usage(usage),
854 T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
855 T::Argument__::Read(usage) => {
856 let reference = self.location_usage(usage)?;
857 charge_gas!(self, charge_read_ref, &reference)?;
858 Ok(reference.read_ref()?)
859 }
860 }
861 }
862
863 pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, Mode::Error>
864 where
865 VMValue: VMValueCast<V>,
866 {
867 let before_height = self.gas_charger.move_gas_status().stack_height_current();
868 let value = self.argument_value(arg)?;
869 let after_height = self.gas_charger.move_gas_status().stack_height_current();
870 debug_assert_eq!(before_height.saturating_add(1), after_height);
871 let value: V = value.cast()?;
872 Ok(value)
873 }
874
875 pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, Mode::Error>
876 where
877 VMValue: VMValueCast<V>,
878 {
879 args.into_iter().map(|arg| self.argument(arg)).collect()
880 }
881
882 pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), Mode::Error> {
883 self.locations
884 .results
885 .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
886 Ok(())
887 }
888
889 pub fn charge_command(
890 &mut self,
891 is_move_call: bool,
892 num_args: usize,
893 num_return: usize,
894 ) -> Result<(), Mode::Error> {
895 let move_gas_status = self.gas_charger.move_gas_status_mut();
896 let before_size = move_gas_status.stack_size_current();
897 let num_popped = if is_move_call {
901 num_args.checked_add(num_return).ok_or_else(|| {
902 make_invariant_violation!("usize overflow when charging gas for command",)
903 })?
904 } else {
905 num_args
906 };
907 move_gas_status
908 .charge(1, 0, num_popped as u64, 0, 1)
909 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
910 let after_size = move_gas_status.stack_size_current();
911 assert_invariant!(
912 before_size == after_size,
913 "We assume currently that the stack size is not decremented. \
914 If this changes, we need to actually account for it here"
915 );
916 Ok(())
917 }
918
919 pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, Mode::Error> {
920 Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
921 }
922
923 pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, Mode::Error> {
924 let id = self.tx_context.borrow_mut().fresh_id();
925 object_runtime_mut!(self)?
926 .new_id(id)
927 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
928 Ok(CtxValue(Value::coin(id, amount)))
929 }
930
931 pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, Mode::Error> {
932 let (id, amount) = coin.0.unpack_coin()?;
933 object_runtime_mut!(self)?
934 .delete_id(id)
935 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
936 Ok(amount)
937 }
938
939 pub fn new_upgrade_cap(&mut self, version_id: ObjectID) -> Result<CtxValue, Mode::Error> {
940 let id = self.tx_context.borrow_mut().fresh_id();
941 object_runtime_mut!(self)?
942 .new_id(id)
943 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
944 let cap = UpgradeCap::new(id, version_id);
945 Ok(CtxValue(Value::upgrade_cap(cap)))
946 }
947
948 pub fn upgrade_receipt(
949 &self,
950 upgrade_ticket: UpgradeTicket,
951 upgraded_package_id: ObjectID,
952 ) -> CtxValue {
953 let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
954 CtxValue(Value::upgrade_receipt(receipt))
955 }
956
957 pub fn vm_move_call(
962 &mut self,
963 function: T::LoadedFunction,
964 args: Vec<CtxValue>,
965 trace_builder_opt: &mut Option<MoveTraceBuilder>,
966 ) -> Result<Vec<CtxValue>, Mode::Error> {
967 with_vm!(self, &function.linkage, Mode::Error, |vm: &mut MoveVM<
968 'env,
969 >| {
970 let ty_args = function
971 .type_arguments
972 .iter()
973 .map(|ty| {
974 let tag: TypeTag = ty.clone().try_into().map_err(|e| {
975 Mode::Error::new_with_source(ExecutionErrorKind::VMInvariantViolation, e)
976 })?;
977 vm.load_type(&tag)
978 .map_err(|e| self.env.convert_linked_vm_error(e, &function.linkage))
979 })
980 .collect::<Result<Vec<_>, Mode::Error>>()?;
981 let result = self.execute_function_bypass_visibility_with_vm(
982 vm,
983 &function.original_mid,
984 &function.name,
985 ty_args,
986 args,
987 &function.linkage,
988 trace_builder_opt,
989 )?;
990 self.take_user_events(
991 vm,
992 function.version_mid,
993 function.definition_index,
994 function.instruction_length,
995 &function.linkage,
996 )?;
997 Ok::<Vec<CtxValue>, Mode::Error>(result)
998 })
999 }
1000
1001 fn execute_function_bypass_visibility_with_vm(
1002 &mut self,
1003 vm: &mut MoveVM<'env>,
1004 original_mid: &ModuleId,
1005 function_name: &IdentStr,
1006 ty_args: Vec<VMType>,
1007 args: Vec<CtxValue>,
1008 linkage: &ExecutableLinkage,
1009 tracer: &mut Option<MoveTraceBuilder>,
1010 ) -> Result<Vec<CtxValue>, Mode::Error> {
1011 let gas_status = self.gas_charger.move_gas_status_mut();
1012 let values = vm
1013 .execute_function_bypass_visibility(
1014 original_mid,
1015 function_name,
1016 ty_args,
1017 args.into_iter().map(|v| v.0.into()).collect(),
1018 &mut SuiGasMeter(gas_status),
1019 tracer.as_mut(),
1020 )
1021 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
1022 Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
1023 }
1024
1025 pub fn deserialize_modules(
1031 &mut self,
1032 module_bytes: &[Vec<u8>],
1033 is_upgrade: bool,
1034 ) -> Result<Vec<CompiledModule>, Mode::Error> {
1035 assert_invariant!(
1036 !module_bytes.is_empty(),
1037 "empty package is checked in transaction input checker"
1038 );
1039 let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
1040 if is_upgrade {
1041 self.gas_charger.charge_upgrade_package(total_bytes)?
1042 } else {
1043 self.gas_charger.charge_publish_package(total_bytes)?
1044 }
1045
1046 let binary_config = self.env.protocol_config.binary_config(None);
1047 let modules = module_bytes
1048 .iter()
1049 .map(|b| {
1050 CompiledModule::deserialize_with_config(b, &binary_config)
1051 .map_err(|e| e.finish(Location::Undefined))
1052 })
1053 .collect::<VMResult<Vec<CompiledModule>>>()
1054 .map_err(|e| self.env.convert_vm_error(e))?;
1055 Ok(modules)
1056 }
1057
1058 fn fetch_package(&mut self, dependency_id: &ObjectID) -> Result<Rc<MovePackage>, Mode::Error> {
1059 let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
1060 |_| {
1061 make_invariant_violation!(
1062 "We should always fetch a single package for each object or return a dependency error."
1063 )
1064 },
1065 )?;
1066 Ok(fetched_package)
1067 }
1068
1069 fn fetch_packages(
1070 &mut self,
1071 dependency_ids: &[ObjectID],
1072 ) -> Result<Vec<Rc<MovePackage>>, Mode::Error> {
1073 let mut fetched = vec![];
1074 let mut missing = vec![];
1075
1076 let dependency_ids: BTreeSet<_> = dependency_ids.iter().collect();
1078
1079 for id in &dependency_ids {
1080 match self.env.linkable_store.get_move_package(id) {
1081 Err(e) => {
1082 return Err(Mode::Error::new_with_source(
1083 ExecutionErrorKind::PublishUpgradeMissingDependency,
1084 e,
1085 ));
1086 }
1087 Ok(Some(inner)) => {
1088 fetched.push(inner);
1089 }
1090 Ok(None) => {
1091 missing.push(*id);
1092 }
1093 }
1094 }
1095
1096 if missing.is_empty() {
1097 assert_invariant!(
1098 fetched.len() == dependency_ids.len(),
1099 "all dependencies should be fetched"
1100 );
1101 Ok(fetched)
1102 } else {
1103 let msg = format!(
1104 "Missing dependencies: {}",
1105 missing
1106 .into_iter()
1107 .map(|dep| format!("{}", dep))
1108 .collect::<Vec<_>>()
1109 .join(", ")
1110 );
1111 Err(Mode::Error::new_with_source(
1112 ExecutionErrorKind::PublishUpgradeMissingDependency,
1113 msg,
1114 ))
1115 }
1116 }
1117
1118 fn publish_and_verify_modules(
1119 &mut self,
1120 package_id: ObjectID,
1121 pkg: &MovePackage,
1122 modules: &[CompiledModule],
1123 linkage: &ExecutableLinkage,
1124 ) -> Result<(VerifiedPackage, MoveVM<'env>), Mode::Error> {
1125 let serialized_pkg = pkg.into_serialized_move_package().map_err(|e| {
1126 make_invariant_violation!("Failed to serialize package for verification: {}", e)
1127 })?;
1128 let data_store = &self.env.linkable_store.package_store;
1129 let vm = self
1130 .env
1131 .vm
1132 .validate_package(
1133 data_store,
1134 *package_id,
1135 serialized_pkg,
1136 &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
1137 self.native_extensions.clone(),
1138 )
1139 .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
1140
1141 for module in modules {
1143 sui_verifier::verifier::sui_verify_module_unmetered(
1146 module,
1147 &BTreeMap::new(),
1148 &self
1149 .env
1150 .protocol_config
1151 .verifier_config(None),
1152 )?;
1153 }
1154
1155 Ok(vm)
1156 }
1157
1158 fn init_modules(
1159 &mut self,
1160 mut vm: MoveVM<'env>,
1161 package_id: ObjectID,
1162 modules: &[CompiledModule],
1163 linkage: &ExecutableLinkage,
1164 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1165 ) -> Result<(), Mode::Error> {
1166 for module in modules {
1167 let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
1168 else {
1169 continue;
1170 };
1171 let fhandle = module.function_handle_at(fdef.function);
1172 let fparameters = module.signature_at(fhandle.parameters);
1173 assert_invariant!(
1174 fparameters.0.len() <= 2,
1175 "init function should have at most 2 parameters"
1176 );
1177 let has_otw = fparameters.0.len() == 2;
1178 let tx_context = self
1179 .location(UsageKind::Borrow, T::Location::TxContext)
1180 .map_err(|e| {
1181 make_invariant_violation!("Failed to get tx context for init function: {}", e)
1182 })?;
1183 charge_gas!(self, charge_store_loc, &tx_context)?;
1185
1186 let args = if has_otw {
1187 vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
1188 } else {
1189 vec![CtxValue(tx_context)]
1190 };
1191 debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
1192 trace_utils::trace_move_call_start(trace_builder_opt);
1193 let return_values = self.execute_function_bypass_visibility_with_vm(
1194 &mut vm,
1195 &module.self_id(),
1196 INIT_FN_NAME,
1197 vec![],
1198 args,
1199 linkage,
1200 trace_builder_opt,
1201 )?;
1202 trace_utils::trace_move_call_end(trace_builder_opt);
1203
1204 let version_mid = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
1205 self.take_user_events(
1206 &vm,
1207 version_mid,
1208 fdef_idx,
1209 fdef.code
1210 .as_ref()
1211 .map(|c| checked_as!(c.code.len(), u16))
1212 .transpose()?
1213 .unwrap_or(0),
1214 linkage,
1215 )?;
1216 assert_invariant!(
1217 return_values.is_empty(),
1218 "init should not have return values"
1219 );
1220 debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
1221 }
1222
1223 Ok(())
1224 }
1225
1226 pub fn publish_and_init_package(
1227 &mut self,
1228 mut modules: Vec<CompiledModule>,
1229 dep_ids: &[ObjectID],
1230 linkage: ResolvedLinkage,
1231 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1232 ) -> Result<ObjectID, Mode::Error> {
1233 let original_id = if Mode::packages_are_predefined() {
1234 (*modules.safe_get(0)?.self_id().address()).into()
1236 } else {
1237 let id = self.tx_context.borrow_mut().fresh_id();
1241 adapter::substitute_package_id(&mut modules, id)?;
1242 id
1243 };
1244
1245 let dependencies = self.fetch_packages(dep_ids)?;
1246 let package = Rc::new(MovePackage::new_initial(
1247 &modules,
1248 self.env.protocol_config,
1249 dependencies.iter().map(|p| p.as_ref()),
1250 )?);
1251 let package_id = package.id();
1252
1253 let linkage = ResolvedLinkage::update_for_publication(package_id, original_id, linkage);
1254
1255 let (pkg, vm) =
1256 self.publish_and_verify_modules(original_id, &package, &modules, &linkage)?;
1257 self.env
1263 .linkable_store
1264 .package_store
1265 .push_package(package_id, package.clone(), pkg)?;
1266
1267 match self.init_modules(vm, package_id, &modules, &linkage, trace_builder_opt) {
1268 Ok(()) => Ok(original_id),
1269 Err(e) => {
1270 self.env
1271 .linkable_store
1272 .package_store
1273 .pop_package(package_id)?;
1274 Err(e)
1275 }
1276 }
1277 }
1278
1279 pub fn upgrade(
1280 &mut self,
1281 mut modules: Vec<CompiledModule>,
1282 dep_ids: &[ObjectID],
1283 current_package_id: ObjectID,
1284 upgrade_ticket_policy: u8,
1285 linkage: ResolvedLinkage,
1286 ) -> Result<ObjectID, Mode::Error> {
1287 let current_move_package = self.fetch_package(¤t_package_id)?;
1289
1290 let original_id = current_move_package.original_package_id();
1291 adapter::substitute_package_id(&mut modules, original_id)?;
1292
1293 let version_id = self.tx_context.borrow_mut().fresh_id();
1298
1299 let dependencies = self.fetch_packages(dep_ids)?;
1300 let package = current_move_package.new_upgraded(
1301 version_id,
1302 &modules,
1303 self.env.protocol_config,
1304 dependencies.iter().map(|p| p.as_ref()),
1305 )?;
1306
1307 let linkage = ResolvedLinkage::update_for_publication(version_id, original_id, linkage);
1308 let (verified_pkg, _) =
1309 self.publish_and_verify_modules(original_id, &package, &modules, &linkage)?;
1310
1311 check_compatibility(
1312 self.env.protocol_config,
1313 current_move_package.as_ref(),
1314 &modules,
1315 upgrade_ticket_policy,
1316 )?;
1317
1318 let current_module_names: BTreeSet<&str> = current_move_package
1321 .serialized_module_map()
1322 .keys()
1323 .map(|s| s.as_str())
1324 .collect();
1325 let upgrade_module_names: BTreeSet<&str> = package
1326 .serialized_module_map()
1327 .keys()
1328 .map(|s| s.as_str())
1329 .collect();
1330 let new_module_names = upgrade_module_names
1331 .difference(¤t_module_names)
1332 .copied()
1333 .collect::<BTreeSet<&str>>();
1334 let new_modules = modules
1335 .iter()
1336 .filter(|m| {
1337 let name = m.identifier_at(m.self_handle().name).as_str();
1338 new_module_names.contains(name)
1339 })
1340 .collect::<Vec<&CompiledModule>>();
1341 let new_module_has_init = new_modules.iter().any(|module| {
1342 module.function_defs.iter().any(|fdef| {
1343 let fhandle = module.function_handle_at(fdef.function);
1344 let fname = module.identifier_at(fhandle.name);
1345 fname == INIT_FN_NAME
1346 })
1347 });
1348 if new_module_has_init {
1349 return Err(Mode::Error::new_with_source(
1351 ExecutionErrorKind::FeatureNotYetSupported,
1352 "`init` in new modules on upgrade is not yet supported",
1353 ));
1354 }
1355
1356 self.env.linkable_store.package_store.push_package(
1357 version_id,
1358 Rc::new(package),
1359 verified_pkg,
1360 )?;
1361 Ok(version_id)
1362 }
1363
1364 pub fn transfer_object(
1369 &mut self,
1370 recipient: Owner,
1371 ty: Type,
1372 object: CtxValue,
1373 ) -> Result<(), Mode::Error> {
1374 self.transfer_object_(recipient, ty, object, false)
1375 }
1376
1377 fn transfer_object_(
1378 &mut self,
1379 recipient: Owner,
1380 ty: Type,
1381 object: CtxValue,
1382 end_of_transaction: bool,
1383 ) -> Result<(), Mode::Error> {
1384 let tag = TypeTag::try_from(ty)
1385 .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1386 let TypeTag::Struct(tag) = tag else {
1387 invariant_violation!("Expected struct type tag");
1388 };
1389 let ty = MoveObjectType::from(*tag);
1390 object_runtime_mut!(self)?
1391 .transfer(recipient, ty, object.0.into(), end_of_transaction)
1392 .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1393 Ok(())
1394 }
1395
1396 #[allow(clippy::type_complexity)]
1401 pub fn argument_updates(
1402 &mut self,
1403 args: Vec<T::Argument>,
1404 ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, Mode::Error> {
1405 args.into_iter()
1406 .filter_map(|arg| self.argument_update(arg).transpose())
1407 .collect()
1408 }
1409
1410 #[allow(clippy::type_complexity)]
1411 fn argument_update(
1412 &mut self,
1413 sp!(_, (arg, ty)): T::Argument,
1414 ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, Mode::Error> {
1415 use sui_types::transaction::Argument as TxArgument;
1416 let ty = match ty {
1417 Type::Reference(true, inner) => (*inner).clone(),
1418 ty => {
1419 debug_assert!(
1420 false,
1421 "Unexpected non reference type in location update: {ty:?}"
1422 );
1423 return Ok(None);
1424 }
1425 };
1426 let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1427 invariant_violation!("unable to generate type tag from type")
1428 };
1429 let location = arg.location();
1430 let resolved = self.locations.resolve(location)?;
1431 let local = match resolved {
1432 ResolvedLocation::Local(local)
1433 | ResolvedLocation::Pure { local, .. }
1434 | ResolvedLocation::Receiving { local, .. } => local,
1435 };
1436 if local.is_invalid()? {
1437 return Ok(None);
1438 }
1439 let value = local.copy()?;
1441 let value = match arg {
1442 T::Argument__::Use(_) => {
1443 value.read_ref()?
1445 }
1446 T::Argument__::Borrow(_, _) => {
1447 value
1449 }
1450 T::Argument__::Freeze(_) => {
1451 invariant_violation!("freeze should not be used for a mutable reference")
1452 }
1453 T::Argument__::Read(_) => {
1454 invariant_violation!("read should not return a reference")
1455 }
1456 };
1457 let layout = self.env.runtime_layout(&ty)?;
1458 let Some(bytes) = value.typed_serialize(&layout) else {
1459 invariant_violation!("Failed to serialize Move value");
1460 };
1461 let arg = match location {
1462 T::Location::TxContext => return Ok(None),
1463 T::Location::GasCoin => TxArgument::GasCoin,
1464 T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1465 T::Location::ObjectInput(i) => TxArgument::Input(
1466 self.locations
1467 .input_object_metadata
1468 .safe_get(i as usize)?
1469 .0
1470 .0,
1471 ),
1472 T::Location::WithdrawalInput(i) => TxArgument::Input(
1473 self.locations
1474 .input_withdrawal_metadata
1475 .safe_get(i as usize)?
1476 .original_input_index
1477 .0,
1478 ),
1479 T::Location::PureInput(i) => TxArgument::Input(
1480 self.locations
1481 .pure_input_metadata
1482 .safe_get(i as usize)?
1483 .original_input_index
1484 .0,
1485 ),
1486 T::Location::ReceivingInput(i) => TxArgument::Input(
1487 self.locations
1488 .receiving_input_metadata
1489 .safe_get(i as usize)?
1490 .original_input_index
1491 .0,
1492 ),
1493 };
1494 Ok(Some((arg, bytes, tag)))
1495 }
1496
1497 pub fn tracked_results(
1498 &self,
1499 results: &[CtxValue],
1500 result_tys: &T::ResultType,
1501 ) -> Result<Vec<(Vec<u8>, TypeTag)>, Mode::Error> {
1502 assert_invariant!(
1503 results.len() == result_tys.len(),
1504 "results and result types should match"
1505 );
1506 results
1507 .iter()
1508 .zip_debug_eq(result_tys)
1509 .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1510 .collect()
1511 }
1512
1513 fn tracked_result(&self, result: &Value, ty: Type) -> Result<(Vec<u8>, TypeTag), Mode::Error> {
1514 let inner_value;
1515 let (v, ty) = match ty {
1516 Type::Reference(_, inner) => {
1517 inner_value = result.copy()?.read_ref()?;
1518 (&inner_value, (*inner).clone())
1519 }
1520 _ => (result, ty),
1521 };
1522 let layout = self.env.runtime_layout(&ty)?;
1523 let Some(bytes) = v.typed_serialize(&layout) else {
1524 invariant_violation!("Failed to serialize Move value");
1525 };
1526 let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1527 invariant_violation!("unable to generate type tag from type")
1528 };
1529 Ok((bytes, tag))
1530 }
1531}
1532
1533impl VMValueCast<CtxValue> for VMValue {
1534 fn cast(self) -> Result<CtxValue, PartialVMError> {
1535 Ok(CtxValue(self.into()))
1536 }
1537}
1538
1539impl CtxValue {
1540 pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1541 Ok(CtxValue(Value::vec_pack(
1542 ty,
1543 values.into_iter().map(|v| v.0).collect(),
1544 )?))
1545 }
1546
1547 pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1548 self.0.coin_ref_value()
1549 }
1550
1551 pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1552 self.0.coin_ref_subtract_balance(amount)
1553 }
1554
1555 pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1556 self.0.coin_ref_add_balance(amount)
1557 }
1558
1559 pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1560 self.0.into_upgrade_ticket()
1561 }
1562
1563 pub fn to_address(&self) -> Result<AccountAddress, ExecutionError> {
1564 self.0.copy()?.cast()
1565 }
1566
1567 pub(super) fn inner_for_tracing(&self) -> &Value {
1569 &self.0
1570 }
1571}
1572
1573fn load_object_arg<Mode: ExecutionMode>(
1574 meter: &mut GasCharger,
1575 env: &Env<Mode>,
1576 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1577 input: T::ObjectInput,
1578) -> Result<(T::InputIndex, InputObjectMetadata, Value), Mode::Error> {
1579 let id = input.arg.id();
1580 let mutability = input.arg.mutability();
1581 let (metadata, value) =
1582 load_object_arg_impl(meter, env, input_object_map, id, mutability, input.ty)?;
1583 Ok((input.original_input_index, metadata, value))
1584}
1585
1586fn load_object_arg_impl<Mode: ExecutionMode>(
1587 meter: &mut GasCharger,
1588 env: &Env<Mode>,
1589 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1590 id: ObjectID,
1591 mutability: ObjectMutability,
1592 ty: T::Type,
1593) -> Result<(InputObjectMetadata, Value), Mode::Error> {
1594 let obj = env.read_object(&id)?;
1595 let owner = obj.owner.clone();
1596 let version = obj.version();
1597 let object_metadata = InputObjectMetadata {
1598 newly_created: false,
1599 id,
1600 mutability,
1601 owner: owner.clone(),
1602 version,
1603 type_: ty.clone(),
1604 };
1605 let sui_types::object::ObjectInner {
1606 data: sui_types::object::Data::Move(move_obj),
1607 ..
1608 } = obj.as_inner()
1609 else {
1610 invariant_violation!("Expected a Move object");
1611 };
1612 assert_expected_move_object_type(&object_metadata.type_, move_obj.type_())?;
1613 let contained_uids = {
1614 let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1615 get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1616 make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1617 })?
1618 };
1619 input_object_map.insert(
1620 id,
1621 object_runtime::InputObject {
1622 contained_uids,
1623 version,
1624 owner,
1625 },
1626 );
1627
1628 let v = Value::deserialize(env, move_obj.contents(), ty)?;
1629 charge_gas_!(meter, env, charge_copy_loc, &v)?;
1630 charge_gas_!(meter, env, charge_store_loc, &v)?;
1631 Ok((object_metadata, v))
1632}
1633
1634fn load_withdrawal_arg<Mode: ExecutionMode>(
1635 meter: &mut GasCharger,
1636 env: &Env<Mode>,
1637 withdrawal: &T::WithdrawalInput,
1638) -> Result<Value, Mode::Error> {
1639 let T::WithdrawalInput {
1640 original_input_index: _,
1641 ty: _,
1642 owner,
1643 amount,
1644 } = withdrawal;
1645 let loaded = Value::funds_accumulator_withdrawal(*owner, *amount);
1646 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1647 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1648 Ok(loaded)
1649}
1650
1651fn load_pure_value<Mode: ExecutionMode>(
1652 meter: &mut GasCharger,
1653 env: &Env<Mode>,
1654 bytes: &[u8],
1655 metadata: &T::PureInput,
1656) -> Result<Value, Mode::Error> {
1657 let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1658 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1660 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1661 Ok(loaded)
1662}
1663
1664fn load_receiving_value<Mode: ExecutionMode>(
1665 meter: &mut GasCharger,
1666 env: &Env<Mode>,
1667 metadata: &T::ReceivingInput,
1668) -> Result<Value, Mode::Error> {
1669 let (id, version, _) = metadata.object_ref;
1670 let loaded = Value::receiving(id, version);
1671 charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1672 charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1673 Ok(loaded)
1674}
1675
1676fn copy_value<Mode: ExecutionMode>(
1677 meter: &mut GasCharger,
1678 env: &Env<Mode>,
1679 value: &Value,
1680) -> Result<Value, Mode::Error> {
1681 charge_gas_!(meter, env, charge_copy_loc, value)?;
1682 charge_gas_!(meter, env, charge_pop, value)?;
1683 Ok(value.copy()?)
1684}
1685
1686fn refund_max_gas_budget<OType>(
1690 writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1691 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1692 gas_charger: &mut GasCharger,
1693 gas_id: ObjectID,
1694 gas_coin_transfer: Option<&GasCoinTransfer>,
1695) -> Result<(), ExecutionError> {
1696 match gas_coin_transfer {
1697 Some(GasCoinTransfer::SendFunds { recipient, .. }) => {
1698 assert_invariant!(
1701 !writes.contains_key(&gas_id),
1702 "Gas coin should not be in writes if it was used with send_funds"
1703 );
1704 balance_change_accumulator_event(
1705 accumulator_events,
1706 *recipient,
1707 checked_as!(gas_charger.gas_budget(), i64)?,
1708 )?;
1709 }
1710 Some(GasCoinTransfer::TransferObjects) | None => {
1711 let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1712 invariant_violation!("Gas object cannot be wrapped or destroyed")
1713 };
1714 let value = std::mem::replace(value_ref, VMValue::u8(0));
1716 let mut locals = Locals::new([Some(value.into())])?;
1717 let mut local = locals.local(0)?;
1718 let coin_value = local.borrow()?.coin_ref_value()?;
1719 let additional = gas_charger.gas_budget();
1720 if coin_value.checked_add(additional).is_none() {
1721 return Err(ExecutionError::new_with_source(
1722 ExecutionErrorKind::CoinBalanceOverflow,
1723 "Gas coin too large after returning the max gas budget",
1724 ));
1725 };
1726 local.borrow()?.coin_ref_add_balance(additional)?;
1727 *value_ref = local.move_()?.into();
1729 }
1730 };
1731 Ok(())
1732}
1733
1734fn finish_gas_coin<OType>(
1740 gas_charger: &mut GasCharger,
1741 writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1742 created_object_ids: &mut IndexSet<ObjectID>,
1743 deleted_object_ids: &IndexSet<ObjectID>,
1744 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1745 gas_id: ObjectID,
1746 gas_payment: GasPayment,
1747 gas_coin_transfer: Option<GasCoinTransfer>,
1748) -> Result<(), ExecutionError> {
1749 refund_max_gas_budget(
1751 writes,
1752 accumulator_events,
1753 gas_charger,
1754 gas_id,
1755 gas_coin_transfer.as_ref(),
1756 )?;
1757
1758 match &gas_coin_transfer {
1764 Some(GasCoinTransfer::SendFunds { recipient, .. }) => {
1765 gas_charger.override_gas_charge_location(PaymentLocation::AddressBalance(
1766 (*recipient).into(),
1767 ))?;
1768 }
1769 Some(GasCoinTransfer::TransferObjects) => {
1770 gas_charger.override_gas_charge_location(PaymentLocation::Coin(gas_id))?;
1771 }
1772 None => (),
1773 }
1774
1775 let address = match gas_payment.location {
1777 PaymentLocation::Coin(_) => {
1778 assert_invariant!(
1780 !matches!(gas_coin_transfer, Some(GasCoinTransfer::SendFunds { .. }))
1781 || deleted_object_ids.contains(&gas_id),
1782 "send_funds transfer implies the coin should be deleted"
1783 );
1784 return Ok(());
1785 }
1786 PaymentLocation::AddressBalance(address) => address,
1787 };
1788
1789 let net_balance_change = if let Some(gas_coin_transfer) = gas_coin_transfer {
1790 match gas_coin_transfer {
1792 GasCoinTransfer::TransferObjects => {
1793 assert_invariant!(
1794 created_object_ids.contains(&gas_id),
1795 "ephemeral coin should be newly created"
1796 );
1797 assert_invariant!(
1798 !deleted_object_ids.contains(&gas_id),
1799 "ephemeral coin should not be deleted if transferred as an object"
1800 );
1801 assert_invariant!(
1802 writes.contains_key(&gas_id),
1803 "ephemeral coin should be in writes if transferred as an object"
1804 );
1805 }
1806 GasCoinTransfer::SendFunds { .. } => {
1807 assert_invariant!(
1808 !created_object_ids.contains(&gas_id),
1809 "ephemeral coin should not be newly created if transferred with send_funds"
1810 );
1811 assert_invariant!(
1812 !deleted_object_ids.contains(&gas_id),
1813 "ephemeral coin should not be deleted if transferred with send_funds"
1814 );
1815 assert_invariant!(
1816 !writes.contains_key(&gas_id),
1817 "ephemeral coin should not be in writes if transferred with send_funds"
1818 );
1819 }
1820 }
1821
1822 let Some(net_balance_change) = gas_payment
1827 .amount
1828 .try_into()
1829 .ok()
1830 .and_then(|i: i64| i.checked_neg())
1831 else {
1832 invariant_violation!("Gas payment amount cannot be represented as i64")
1833 };
1834 net_balance_change
1835 } else {
1836 let was_created = created_object_ids.shift_remove(&gas_id);
1840 assert_invariant!(was_created, "ephemeral coin should be newly created");
1841 let Some((_owner, _ty, value)) = writes.shift_remove(&gas_id) else {
1842 invariant_violation!("checked above that the gas coin was present")
1843 };
1844 let (_id, remaining_balance) = Value::from(value).unpack_coin()?;
1845 let Some(net_balance_change): Option<i64> = (remaining_balance as i128)
1849 .checked_sub(gas_payment.amount as i128)
1850 .and_then(|i| i.try_into().ok())
1851 else {
1852 invariant_violation!("Remaining balance could not be represented as i64")
1853 };
1854 net_balance_change
1855 };
1856 balance_change_accumulator_event(accumulator_events, address.into(), net_balance_change)?;
1857 Ok(())
1858}
1859
1860fn balance_change_accumulator_event(
1861 accumulator_events: &mut Vec<MoveAccumulatorEvent>,
1862 address: AccountAddress,
1863 balance_change: i64,
1864) -> Result<(), ExecutionError> {
1865 if balance_change == 0 {
1866 return Ok(());
1867 }
1868 let balance_type = Balance::type_tag(sui_types::gas_coin::GAS::type_tag());
1869 let Some(accumulator_id) =
1870 accumulator_root::AccumulatorValue::get_field_id(address.into(), &balance_type).ok()
1871 else {
1872 invariant_violation!("Failed to compute accumulator field id")
1873 };
1874 let (action, value) = if balance_change < 0 {
1875 (
1876 MoveAccumulatorAction::Split,
1877 MoveAccumulatorValue::U64(balance_change.unsigned_abs()),
1878 )
1879 } else {
1880 (
1881 MoveAccumulatorAction::Merge,
1882 MoveAccumulatorValue::U64(balance_change as u64),
1883 )
1884 };
1885 accumulator_events.push(MoveAccumulatorEvent {
1886 accumulator_id: *accumulator_id.inner(),
1887 action,
1888 target_addr: address,
1889 target_ty: balance_type,
1890 value,
1891 });
1892 Ok(())
1893}
1894
1895unsafe fn create_written_object<Mode: ExecutionMode>(
1901 env: &Env<Mode>,
1902 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1903 id: ObjectID,
1904 type_: Type,
1905 has_public_transfer: bool,
1906 contents: Vec<u8>,
1907) -> Result<MoveObject, ExecutionError> {
1908 debug_assert_eq!(
1909 id,
1910 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1911 );
1912 let old_obj_ver = objects_modified_at
1913 .get(&id)
1914 .map(|obj: &LoadedRuntimeObject| obj.version);
1915
1916 let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
1917 invariant_violation!("unable to generate type tag from type")
1918 };
1919
1920 let struct_tag = match type_tag {
1921 TypeTag::Struct(inner) => *inner,
1922 _ => invariant_violation!("Non struct type for object"),
1923 };
1924 unsafe {
1925 MoveObject::new_from_execution(
1926 struct_tag.into(),
1927 has_public_transfer,
1928 old_obj_ver.unwrap_or_default(),
1929 contents,
1930 env.protocol_config,
1931 Mode::packages_are_predefined(),
1932 )
1933 }
1934}
1935
1936pub fn subst_signature(
1938 signature: LoadedFunctionInformation,
1939 type_arguments: &[VMType],
1940) -> VMResult<LoadedFunctionInformation> {
1941 let LoadedFunctionInformation {
1942 parameters,
1943 return_,
1944 is_entry,
1945 is_native,
1946 visibility,
1947 index,
1948 instruction_count,
1949 } = signature;
1950 let parameters = parameters
1951 .into_iter()
1952 .map(|ty| ty.subst(type_arguments))
1953 .collect::<PartialVMResult<Vec<_>>>()
1954 .map_err(|err| err.finish(Location::Undefined))?;
1955 let return_ = return_
1956 .into_iter()
1957 .map(|ty| ty.subst(type_arguments))
1958 .collect::<PartialVMResult<Vec<_>>>()
1959 .map_err(|err| err.finish(Location::Undefined))?;
1960 Ok(LoadedFunctionInformation {
1961 parameters,
1962 return_,
1963 is_entry,
1964 is_native,
1965 visibility,
1966 index,
1967 instruction_count,
1968 })
1969}
1970
1971pub enum EitherError<E: ExecutionErrorTrait = ExecutionError> {
1972 CommandArgument(CommandArgumentError),
1973 Execution(E),
1974}
1975
1976impl<E: ExecutionErrorTrait> From<ExecutionError> for EitherError<E> {
1977 fn from(e: ExecutionError) -> Self {
1978 EitherError::Execution(e.into())
1979 }
1980}
1981
1982impl<E: ExecutionErrorTrait> From<CommandArgumentError> for EitherError<E> {
1983 fn from(e: CommandArgumentError) -> Self {
1984 EitherError::CommandArgument(e)
1985 }
1986}
1987
1988impl<E: ExecutionErrorTrait> EitherError<E> {
1989 pub fn into_execution_error(self, command_index: usize) -> E {
1990 match self {
1991 EitherError::CommandArgument(e) => command_argument_error(e, command_index).into(),
1992 EitherError::Execution(e) => e,
1993 }
1994 }
1995}
1996
1997#[derive(Debug)]
2005pub enum PrimitiveArgumentLayout {
2006 Option(Box<PrimitiveArgumentLayout>),
2008 Vector(Box<PrimitiveArgumentLayout>),
2010 Ascii,
2012 UTF8,
2014 Bool,
2016 U8,
2017 U16,
2018 U32,
2019 U64,
2020 U128,
2021 U256,
2022 Address,
2023}
2024
2025impl PrimitiveArgumentLayout {
2026 pub fn bcs_only(&self) -> bool {
2030 match self {
2031 PrimitiveArgumentLayout::Option(_)
2033 | PrimitiveArgumentLayout::Ascii
2034 | PrimitiveArgumentLayout::UTF8 => false,
2035 PrimitiveArgumentLayout::Bool
2037 | PrimitiveArgumentLayout::U8
2038 | PrimitiveArgumentLayout::U16
2039 | PrimitiveArgumentLayout::U32
2040 | PrimitiveArgumentLayout::U64
2041 | PrimitiveArgumentLayout::U128
2042 | PrimitiveArgumentLayout::U256
2043 | PrimitiveArgumentLayout::Address => true,
2044 PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
2046 }
2047 }
2048}
2049
2050pub fn bcs_argument_validate(
2054 bytes: &[u8],
2055 idx: u16,
2056 layout: PrimitiveArgumentLayout,
2057) -> Result<(), ExecutionError> {
2058 bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
2059 ExecutionError::new_with_source(
2060 ExecutionErrorKind::command_argument_error(CommandArgumentError::InvalidBCSBytes, idx),
2061 format!("Function expects {layout} but provided argument's value does not match",),
2062 )
2063 })
2064}
2065
2066impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
2067 type Value = ();
2068 fn deserialize<D: serde::de::Deserializer<'d>>(
2069 self,
2070 deserializer: D,
2071 ) -> Result<Self::Value, D::Error> {
2072 use serde::de::Error;
2073 match self {
2074 PrimitiveArgumentLayout::Ascii => {
2075 let s: &str = serde::Deserialize::deserialize(deserializer)?;
2076 if !s.is_ascii() {
2077 Err(D::Error::custom("not an ascii string"))
2078 } else {
2079 Ok(())
2080 }
2081 }
2082 PrimitiveArgumentLayout::UTF8 => {
2083 deserializer.deserialize_string(serde::de::IgnoredAny)?;
2084 Ok(())
2085 }
2086 PrimitiveArgumentLayout::Option(layout) => {
2087 deserializer.deserialize_option(OptionElementVisitor(layout))
2088 }
2089 PrimitiveArgumentLayout::Vector(layout) => {
2090 deserializer.deserialize_seq(VectorElementVisitor(layout))
2091 }
2092 PrimitiveArgumentLayout::Bool => {
2095 deserializer.deserialize_bool(serde::de::IgnoredAny)?;
2096 Ok(())
2097 }
2098 PrimitiveArgumentLayout::U8 => {
2099 deserializer.deserialize_u8(serde::de::IgnoredAny)?;
2100 Ok(())
2101 }
2102 PrimitiveArgumentLayout::U16 => {
2103 deserializer.deserialize_u16(serde::de::IgnoredAny)?;
2104 Ok(())
2105 }
2106 PrimitiveArgumentLayout::U32 => {
2107 deserializer.deserialize_u32(serde::de::IgnoredAny)?;
2108 Ok(())
2109 }
2110 PrimitiveArgumentLayout::U64 => {
2111 deserializer.deserialize_u64(serde::de::IgnoredAny)?;
2112 Ok(())
2113 }
2114 PrimitiveArgumentLayout::U128 => {
2115 deserializer.deserialize_u128(serde::de::IgnoredAny)?;
2116 Ok(())
2117 }
2118 PrimitiveArgumentLayout::U256 => {
2119 U256::deserialize(deserializer)?;
2120 Ok(())
2121 }
2122 PrimitiveArgumentLayout::Address => {
2123 SuiAddress::deserialize(deserializer)?;
2124 Ok(())
2125 }
2126 }
2127 }
2128}
2129
2130struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2131
2132impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
2133 type Value = ();
2134
2135 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2136 formatter.write_str("Vector")
2137 }
2138
2139 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
2140 where
2141 A: serde::de::SeqAccess<'d>,
2142 {
2143 while seq.next_element_seed(self.0)?.is_some() {}
2144 Ok(())
2145 }
2146}
2147
2148struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2149
2150impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
2151 type Value = ();
2152
2153 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2154 formatter.write_str("Option")
2155 }
2156
2157 fn visit_none<E>(self) -> Result<Self::Value, E>
2158 where
2159 E: serde::de::Error,
2160 {
2161 Ok(())
2162 }
2163
2164 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
2165 where
2166 D: serde::Deserializer<'d>,
2167 {
2168 self.0.deserialize(deserializer)
2169 }
2170}
2171
2172impl fmt::Display for PrimitiveArgumentLayout {
2173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2174 match self {
2175 PrimitiveArgumentLayout::Vector(inner) => {
2176 write!(f, "vector<{inner}>")
2177 }
2178 PrimitiveArgumentLayout::Option(inner) => {
2179 write!(f, "std::option::Option<{inner}>")
2180 }
2181 PrimitiveArgumentLayout::Ascii => {
2182 write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
2183 }
2184 PrimitiveArgumentLayout::UTF8 => {
2185 write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
2186 }
2187 PrimitiveArgumentLayout::Bool => write!(f, "bool"),
2188 PrimitiveArgumentLayout::U8 => write!(f, "u8"),
2189 PrimitiveArgumentLayout::U16 => write!(f, "u16"),
2190 PrimitiveArgumentLayout::U32 => write!(f, "u32"),
2191 PrimitiveArgumentLayout::U64 => write!(f, "u64"),
2192 PrimitiveArgumentLayout::U128 => write!(f, "u128"),
2193 PrimitiveArgumentLayout::U256 => write!(f, "u256"),
2194 PrimitiveArgumentLayout::Address => write!(f, "address"),
2195 }
2196 }
2197}
2198
2199pub fn finish(
2200 protocol_config: &ProtocolConfig,
2201 state_view: &dyn ExecutionState,
2202 gas_charger: &mut GasCharger,
2203 tx_context: &TxContext,
2204 by_value_shared_objects: &BTreeSet<ObjectID>,
2205 consensus_owner_objects: &BTreeMap<ObjectID, Owner>,
2206 loaded_runtime_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
2207 written_objects: BTreeMap<ObjectID, Object>,
2208 created_object_ids: IndexSet<ObjectID>,
2209 deleted_object_ids: IndexSet<ObjectID>,
2210 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
2211 accumulator_events: Vec<MoveAccumulatorEvent>,
2212 settlement_input_sui: u64,
2213 settlement_output_sui: u64,
2214) -> Result<ExecutionResults, ExecutionError> {
2215 for id in by_value_shared_objects {
2220 if let Some(obj) = written_objects.get(id) {
2223 if !obj.is_shared() {
2224 return Err(ExecutionError::new(
2225 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2226 Some(
2227 format!(
2228 "Shared object operation on {} not allowed: \
2229 cannot be frozen, transferred, or wrapped",
2230 id
2231 )
2232 .into(),
2233 ),
2234 ));
2235 }
2236 } else {
2237 if !deleted_object_ids.contains(id) {
2240 return Err(ExecutionError::new(
2241 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2242 Some(
2243 format!(
2244 "Shared object operation on {} not allowed: \
2245 shared objects used by value must be re-shared if not deleted",
2246 id
2247 )
2248 .into(),
2249 ),
2250 ));
2251 }
2252 }
2253 }
2254
2255 for (id, original_owner) in consensus_owner_objects {
2257 let Owner::ConsensusAddressOwner { owner, .. } = original_owner else {
2258 panic!(
2259 "verified before adding to `consensus_owner_objects` that these are ConsensusAddressOwner"
2260 );
2261 };
2262 if tx_context.sender() != *owner {
2265 debug_fatal!(
2266 "transaction with a singly owned input object where the tx sender is not the owner should never be executed"
2267 );
2268 return Err(ExecutionError::new(
2269 ExecutionErrorKind::SharedObjectOperationNotAllowed,
2270 Some(
2271 format!(
2272 "Shared object operation on {} not allowed: \
2273 transaction with singly owned input object must be sent by the owner",
2274 id
2275 )
2276 .into(),
2277 ),
2278 ));
2279 }
2280 }
2288
2289 let user_events: Vec<Event> = user_events
2290 .into_iter()
2291 .map(|(module_id, tag, contents)| {
2292 Event::new(
2293 module_id.address(),
2294 module_id.name(),
2295 tx_context.sender(),
2296 tag,
2297 contents,
2298 )
2299 })
2300 .collect();
2301
2302 let mut receiving_funds_type_and_owners = BTreeMap::new();
2303 let accumulator_events = accumulator_events
2304 .into_iter()
2305 .map(|accum_event| {
2306 if let Some(ty) = Balance::maybe_get_balance_type_param(&accum_event.target_ty) {
2307 receiving_funds_type_and_owners
2308 .entry(ty)
2309 .or_insert_with(BTreeSet::new)
2310 .insert(accum_event.target_addr.into());
2311 }
2312 let value = match accum_event.value {
2313 MoveAccumulatorValue::U64(amount) => AccumulatorValue::Integer(amount),
2314 MoveAccumulatorValue::EventRef(event_idx) => {
2315 let Some(event) = user_events.get(checked_as!(event_idx, usize)?) else {
2316 invariant_violation!(
2317 "Could not find authenticated event at index {}",
2318 event_idx
2319 );
2320 };
2321 let digest = event.digest();
2322 AccumulatorValue::EventDigest(nonempty![(event_idx, digest)])
2323 }
2324 };
2325
2326 let address =
2327 AccumulatorAddress::new(accum_event.target_addr.into(), accum_event.target_ty);
2328
2329 let write = AccumulatorWriteV1 {
2330 address,
2331 operation: accum_event.action.into_sui_accumulator_action(),
2332 value,
2333 };
2334
2335 Ok(AccumulatorEvent::new(
2336 AccumulatorObjId::new_unchecked(accum_event.accumulator_id),
2337 write,
2338 ))
2339 })
2340 .collect::<Result<Vec<_>, ExecutionError>>()?;
2341
2342 for object in written_objects.values() {
2344 let coin_type = object.type_().and_then(|ty| ty.coin_type_maybe());
2345 let owner = object.owner.get_owner_address();
2346 if let (Some(ty), Ok(owner)) = (coin_type, owner) {
2347 receiving_funds_type_and_owners
2348 .entry(ty)
2349 .or_insert_with(BTreeSet::new)
2350 .insert(owner);
2351 }
2352 }
2353 let DenyListResult {
2354 result,
2355 num_non_gas_coin_owners,
2356 } = state_view.check_coin_deny_list(receiving_funds_type_and_owners);
2357 gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
2358 result?;
2359
2360 let created_object_ids: BTreeSet<ObjectID> = created_object_ids.into_iter().collect();
2361 let deleted_object_ids: BTreeSet<ObjectID> = deleted_object_ids.into_iter().collect();
2362 let modified_objects: BTreeSet<ObjectID> = loaded_runtime_objects
2363 .into_iter()
2364 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
2365 .collect();
2366
2367 assert_invariant!(
2368 created_object_ids.is_disjoint(&deleted_object_ids),
2369 "Created and deleted object sets should be disjoint"
2370 );
2371 assert_invariant!(
2372 modified_objects.is_disjoint(&created_object_ids),
2373 "Modified and created object sets should be disjoint"
2374 );
2375 assert_invariant!(
2376 written_objects
2377 .keys()
2378 .all(|id| !deleted_object_ids.contains(id)),
2379 "Written objects should not be deleted"
2380 );
2381 Ok(ExecutionResults::V2(ExecutionResultsV2 {
2382 written_objects,
2383 modified_objects,
2384 created_object_ids,
2385 deleted_object_ids,
2386 user_events,
2387 accumulator_events,
2388 settlement_input_sui,
2389 settlement_output_sui,
2390 }))
2391}
2392
2393pub fn fetch_package(
2394 state_view: &impl BackingPackageStore,
2395 package_id: &ObjectID,
2396) -> Result<PackageObject, ExecutionError> {
2397 let mut fetched_packages = fetch_packages(state_view, vec![package_id])?;
2398 assert_invariant!(
2399 fetched_packages.len() == 1,
2400 "Number of fetched packages must match the number of package object IDs if successful."
2401 );
2402 match fetched_packages.pop() {
2403 Some(pkg) => Ok(pkg),
2404 None => invariant_violation!(
2405 "We should always fetch a package for each object or return a dependency error."
2406 ),
2407 }
2408}
2409
2410pub fn fetch_packages<'ctx, 'state>(
2411 state_view: &'state impl BackingPackageStore,
2412 package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
2413) -> Result<Vec<PackageObject>, ExecutionError> {
2414 let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
2415 match get_package_objects(state_view, package_ids) {
2416 Err(e) => Err(ExecutionError::new_with_source(
2417 ExecutionErrorKind::PublishUpgradeMissingDependency,
2418 e,
2419 )),
2420 Ok(Err(missing_deps)) => {
2421 let msg = format!(
2422 "Missing dependencies: {}",
2423 missing_deps
2424 .into_iter()
2425 .map(|dep| format!("{}", dep))
2426 .collect::<Vec<_>>()
2427 .join(", ")
2428 );
2429 Err(ExecutionError::new_with_source(
2430 ExecutionErrorKind::PublishUpgradeMissingDependency,
2431 msg,
2432 ))
2433 }
2434 Ok(Ok(pkgs)) => Ok(pkgs),
2435 }
2436}
2437
2438pub fn check_compatibility(
2439 protocol_config: &ProtocolConfig,
2440 existing_package: &MovePackage,
2441 upgrading_modules: &[CompiledModule],
2442 policy: u8,
2443) -> Result<(), ExecutionError> {
2444 let Ok(policy) = UpgradePolicy::try_from(policy) else {
2446 return Err(ExecutionError::from_kind(
2447 ExecutionErrorKind::PackageUpgradeError {
2448 upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
2449 },
2450 ));
2451 };
2452
2453 let pool = &mut normalized::RcPool::new();
2454 let binary_config = protocol_config.binary_config(None);
2455 let Ok(current_normalized) =
2456 existing_package.normalize(pool, &binary_config, true)
2457 else {
2458 invariant_violation!("Tried to normalize modules in existing package but failed")
2459 };
2460
2461 let existing_modules_len = current_normalized.len();
2462 let upgrading_modules_len = upgrading_modules.len();
2463 let disallow_new_modules = policy as u8 == UpgradePolicy::DEP_ONLY;
2464
2465 if disallow_new_modules && existing_modules_len != upgrading_modules_len {
2466 return Err(ExecutionError::new_with_source(
2467 ExecutionErrorKind::PackageUpgradeError {
2468 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2469 },
2470 format!(
2471 "Existing package has {existing_modules_len} modules, but new package has \
2472 {upgrading_modules_len}. Adding or removing a module to a deps only package is not allowed."
2473 ),
2474 ));
2475 }
2476
2477 let mut new_normalized = normalize_deserialized_modules(
2478 pool,
2479 upgrading_modules.iter(),
2480 true,
2481 );
2482 for (name, cur_module) in current_normalized {
2483 let Some(new_module) = new_normalized.remove(&name) else {
2484 return Err(ExecutionError::new_with_source(
2485 ExecutionErrorKind::PackageUpgradeError {
2486 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2487 },
2488 format!("Existing module {name} not found in next version of package"),
2489 ));
2490 };
2491
2492 check_module_compatibility(&policy, &cur_module, &new_module)?;
2493 }
2494
2495 debug_assert!(!disallow_new_modules || new_normalized.is_empty());
2497
2498 Ok(())
2499}
2500
2501fn check_module_compatibility(
2502 policy: &UpgradePolicy,
2503 cur_module: &move_binary_format::compatibility::Module,
2504 new_module: &move_binary_format::compatibility::Module,
2505) -> Result<(), ExecutionError> {
2506 match policy {
2507 UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
2508 UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
2509 UpgradePolicy::Compatible => {
2510 let compatibility = Compatibility::upgrade_check();
2511
2512 compatibility.check(cur_module, new_module)
2513 }
2514 }
2515 .map_err(|e| {
2516 ExecutionError::new_with_source(
2517 ExecutionErrorKind::PackageUpgradeError {
2518 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
2519 },
2520 e,
2521 )
2522 })
2523}
2524
2525fn assert_expected_move_object_type(
2528 actual: &Type,
2529 expected: &MoveObjectType,
2530) -> Result<(), ExecutionError> {
2531 let Type::Datatype(actual) = actual else {
2532 invariant_violation!("Expected a datatype for a Move object");
2533 };
2534 let (a, m, n) = actual.qualified_ident();
2535 assert_invariant!(
2536 a == &expected.address(),
2537 "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
2538 );
2539 assert_invariant!(
2540 m == expected.module(),
2541 "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
2542 );
2543 assert_invariant!(
2544 n == expected.name(),
2545 "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
2546 );
2547 let actual_type_arguments = &actual.type_arguments;
2548 let expected_type_arguments = expected.type_params();
2549 assert_invariant!(
2550 actual_type_arguments.len() == expected_type_arguments.len(),
2551 "Actual type arg length does not match expected. \
2552 actual: {actual:?} vs expected: {expected:?}",
2553 );
2554 for (actual_ty, expected_ty) in actual_type_arguments
2555 .iter()
2556 .zip_debug_eq(&expected_type_arguments)
2557 {
2558 assert_expected_type(actual_ty, expected_ty)?;
2559 }
2560 Ok(())
2561}
2562
2563fn assert_expected_type(actual: &Type, expected: &TypeTag) -> Result<(), ExecutionError> {
2566 match (actual, expected) {
2567 (Type::Bool, TypeTag::Bool)
2568 | (Type::U8, TypeTag::U8)
2569 | (Type::U16, TypeTag::U16)
2570 | (Type::U32, TypeTag::U32)
2571 | (Type::U64, TypeTag::U64)
2572 | (Type::U128, TypeTag::U128)
2573 | (Type::U256, TypeTag::U256)
2574 | (Type::Address, TypeTag::Address)
2575 | (Type::Signer, TypeTag::Signer) => Ok(()),
2576 (Type::Vector(inner_actual), TypeTag::Vector(inner_expected)) => {
2577 assert_expected_type(&inner_actual.element_type, inner_expected)
2578 }
2579 (Type::Datatype(actual_dt), TypeTag::Struct(expected_st)) => {
2580 assert_expected_data_type(actual_dt, expected_st)
2581 }
2582 _ => invariant_violation!(
2583 "Type mismatch between actual: {actual:?} and expected: {expected:?}"
2584 ),
2585 }
2586}
2587fn assert_expected_data_type(
2590 actual: &Datatype,
2591 expected: &StructTag,
2592) -> Result<(), ExecutionError> {
2593 let (a, m, n) = actual.qualified_ident();
2594 assert_invariant!(
2595 a == &expected.address,
2596 "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
2597 );
2598 assert_invariant!(
2599 m == expected.module.as_ident_str(),
2600 "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
2601 );
2602 assert_invariant!(
2603 n == expected.name.as_ident_str(),
2604 "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
2605 );
2606 let actual_type_arguments = &actual.type_arguments;
2607 let expected_type_arguments = &expected.type_params;
2608 assert_invariant!(
2609 actual_type_arguments.len() == expected_type_arguments.len(),
2610 "Actual type arg length does not match expected. \
2611 actual: {actual:?} vs expected: {expected:?}",
2612 );
2613 for (actual_ty, expected_ty) in actual_type_arguments
2614 .iter()
2615 .zip_debug_eq(expected_type_arguments)
2616 {
2617 assert_expected_type(actual_ty, expected_ty)?;
2618 }
2619 Ok(())
2620}