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