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