1pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7#[allow(clippy::type_complexity)]
8mod checked {
9 use crate::{
10 adapter::new_native_extensions,
11 data_store::{
12 PackageStore,
13 cached_package_store::CachedPackageStore,
14 legacy::{linkage_view::LinkageView, sui_data_store::SuiDataStore},
15 },
16 execution_mode::ExecutionMode,
17 execution_value::{
18 CommandKind, ExecutionState, InputObjectMetadata, InputValue, Mutability,
19 ObjectContents, ObjectValue, RawValueType, ResultValue, SizeBound, TryFromValue,
20 UsageKind, Value,
21 },
22 gas_charger::GasCharger,
23 gas_meter::SuiGasMeter,
24 type_resolver::TypeTagResolver,
25 };
26 use indexmap::IndexSet;
27 use move_binary_format::{
28 CompiledModule,
29 errors::{Location, PartialVMError, VMError, VMResult},
30 file_format::{AbilitySet, CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
31 };
32 use move_core_types::{
33 account_address::AccountAddress,
34 identifier::IdentStr,
35 language_storage::{ModuleId, StructTag, TypeTag},
36 resolver::MoveResolver,
37 u256::U256,
38 vm_status::StatusCode,
39 };
40 use move_trace_format::format::MoveTraceBuilder;
41 use move_vm_runtime::{
42 move_vm::MoveVM,
43 native_extensions::NativeContextExtensions,
44 session::{LoadedFunctionInstantiation, SerializedReturnValues},
45 };
46 use move_vm_types::loaded_data::runtime_types::Type;
47 use mysten_common::debug_fatal;
48 use nonempty::nonempty;
49 use std::{
50 borrow::Borrow,
51 cell::RefCell,
52 collections::{BTreeMap, BTreeSet, HashMap},
53 rc::Rc,
54 sync::Arc,
55 };
56 use sui_move_natives::object_runtime::{
57 self, LoadedRuntimeObject, MoveAccumulatorEvent, MoveAccumulatorValue, ObjectRuntime,
58 RuntimeResults, get_all_uids, max_event_error,
59 };
60 use sui_protocol_config::ProtocolConfig;
61 use sui_types::{
62 accumulator_event::AccumulatorEvent,
63 accumulator_root::AccumulatorObjId,
64 balance::Balance,
65 base_types::{MoveObjectType, ObjectID, SuiAddress, TxContext},
66 coin::Coin,
67 effects::{AccumulatorAddress, AccumulatorValue, AccumulatorWriteV1},
68 error::{ExecutionError, ExecutionErrorKind, SuiError, command_argument_error},
69 event::Event,
70 execution::{ExecutionResults, ExecutionResultsV2},
71 execution_status::CommandArgumentError,
72 funds_accumulator::Withdrawal,
73 metrics::LimitsMetrics,
74 move_package::MovePackage,
75 object::{Data, MoveObject, Object, ObjectInner, Owner},
76 storage::DenyListResult,
77 transaction::{
78 Argument, CallArg, FundsWithdrawalArg, ObjectArg, SharedObjectMutability, WithdrawFrom,
79 },
80 };
81 use tracing::instrument;
82
83 pub struct ExecutionContext<'vm, 'state, 'a> {
85 pub protocol_config: &'a ProtocolConfig,
87 pub metrics: Arc<LimitsMetrics>,
89 pub vm: &'vm MoveVM,
91 pub linkage_view: LinkageView<'state>,
93 pub native_extensions: NativeContextExtensions<'state>,
94 pub state_view: &'state dyn ExecutionState,
96 pub tx_context: Rc<RefCell<TxContext>>,
99 pub gas_charger: &'a mut GasCharger,
101 additional_transfers: Vec<(SuiAddress, ObjectValue)>,
103 new_packages: Vec<MovePackage>,
105 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
107 gas: InputValue,
110 inputs: Vec<InputValue>,
112 results: Vec<Vec<ResultValue>>,
116 borrowed: HashMap<Arg, bool>,
119 per_command_by_value_shared_objects: BTreeSet<ObjectID>,
121 }
122
123 struct AdditionalWrite {
125 recipient: Owner,
127 type_: Type,
129 has_public_transfer: bool,
131 bytes: Vec<u8>,
133 }
134
135 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
136 pub struct Arg(Arg_);
137
138 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
139 enum Arg_ {
140 V1(Argument),
141 V2(NormalizedArg),
142 }
143
144 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
145 enum NormalizedArg {
146 GasCoin,
147 Input(u16),
148 Result(u16, u16),
149 }
150
151 impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
152 #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
153 pub fn new(
154 protocol_config: &'a ProtocolConfig,
155 metrics: Arc<LimitsMetrics>,
156 vm: &'vm MoveVM,
157 state_view: &'state dyn ExecutionState,
158 tx_context: Rc<RefCell<TxContext>>,
159 gas_charger: &'a mut GasCharger,
160 inputs: Vec<CallArg>,
161 ) -> Result<Self, ExecutionError>
162 where
163 'a: 'state,
164 {
165 let mut linkage_view = LinkageView::new(Box::new(CachedPackageStore::new(Box::new(
166 state_view.as_sui_resolver(),
167 ))));
168 let mut input_object_map = BTreeMap::new();
169 let tx_context_ref = RefCell::borrow(&tx_context);
170 let inputs = inputs
171 .into_iter()
172 .map(|call_arg| {
173 load_call_arg(
174 protocol_config,
175 vm,
176 state_view,
177 &mut linkage_view,
178 &[],
179 &mut input_object_map,
180 &tx_context_ref,
181 call_arg,
182 )
183 })
184 .collect::<Result<_, ExecutionError>>()?;
185 std::mem::drop(tx_context_ref);
186 let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
187 let mut gas = load_object(
188 protocol_config,
189 vm,
190 state_view,
191 &mut linkage_view,
192 &[],
193 &mut input_object_map,
194 None,
195 gas_coin,
196 )?;
197 let Some(Value::Object(ObjectValue {
201 contents: ObjectContents::Coin(coin),
202 ..
203 })) = &mut gas.inner.value
204 else {
205 invariant_violation!("Gas object should be a populated coin")
206 };
207
208 let max_gas_in_balance = gas_charger.gas_budget();
209 let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
210 invariant_violation!(
211 "Transaction input checker should check that there is enough gas"
212 );
213 };
214 coin.balance = Balance::new(new_balance);
215 gas
216 } else {
217 InputValue {
218 object_metadata: None,
219 inner: ResultValue {
220 last_usage_kind: None,
221 value: None,
222 shared_object_ids: BTreeSet::new(),
223 },
224 }
225 };
226 let native_extensions = new_native_extensions(
227 state_view.as_child_resolver(),
228 input_object_map,
229 !gas_charger.is_unmetered(),
230 protocol_config,
231 metrics.clone(),
232 tx_context.clone(),
233 );
234
235 Ok(Self {
236 protocol_config,
237 metrics,
238 vm,
239 linkage_view,
240 native_extensions,
241 state_view,
242 tx_context,
243 gas_charger,
244 gas,
245 inputs,
246 results: vec![],
247 additional_transfers: vec![],
248 new_packages: vec![],
249 user_events: vec![],
250 borrowed: HashMap::new(),
251 per_command_by_value_shared_objects: BTreeSet::new(),
252 })
253 }
254
255 pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
256 self.native_extensions
257 .get::<ObjectRuntime>()
258 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
259 }
260
261 pub fn fresh_id(&mut self) -> Result<ObjectID, ExecutionError> {
263 let object_id = self.tx_context.borrow_mut().fresh_id();
264 self.native_extensions
265 .get_mut()
266 .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.new_id(object_id))
267 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
268 Ok(object_id)
269 }
270
271 pub fn delete_id(&mut self, object_id: ObjectID) -> Result<(), ExecutionError> {
273 self.native_extensions
274 .get_mut()
275 .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.delete_id(object_id))
276 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
277 }
278
279 pub fn set_link_context(
282 &mut self,
283 package_id: ObjectID,
284 ) -> Result<AccountAddress, ExecutionError> {
285 if self.linkage_view.has_linkage(package_id)? {
286 return Ok(self
288 .linkage_view
289 .original_package_id()?
290 .unwrap_or(*package_id));
291 }
292
293 let move_package = get_package(&self.linkage_view, package_id)
294 .map_err(|e| self.convert_vm_error(e))?;
295
296 self.linkage_view.set_linkage(&move_package)
297 }
298
299 pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
301 load_type(self.vm, &self.linkage_view, &self.new_packages, type_tag)
302 }
303
304 pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
306 load_type_from_struct(self.vm, &self.linkage_view, &self.new_packages, struct_tag)
307 }
308
309 pub fn get_type_abilities(&self, t: &Type) -> Result<AbilitySet, ExecutionError> {
310 self.vm
311 .get_runtime()
312 .get_type_abilities(t)
313 .map_err(|e| self.convert_vm_error(e))
314 }
315
316 pub fn take_user_events(
319 &mut self,
320 module_id: &ModuleId,
321 function: FunctionDefinitionIndex,
322 last_offset: CodeOffset,
323 ) -> Result<(), ExecutionError> {
324 let events = self
325 .native_extensions
326 .get_mut()
327 .map(|object_runtime: &mut ObjectRuntime| object_runtime.take_user_events())
328 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
329 let num_events = self.user_events.len() + events.len();
330 let max_events = self.protocol_config.max_num_event_emit();
331 if num_events as u64 > max_events {
332 let err = max_event_error(max_events)
333 .at_code_offset(function, last_offset)
334 .finish(Location::Module(module_id.clone()));
335 return Err(self.convert_vm_error(err));
336 }
337 let new_events = events
338 .into_iter()
339 .map(|(tag, value)| {
340 let type_tag = TypeTag::Struct(Box::new(tag.clone()));
341 let ty = unwrap_type_tag_load(
342 self.protocol_config,
343 self.vm
344 .get_runtime()
345 .try_load_cached_type(&type_tag)
346 .map_err(|e| self.convert_vm_error(e))?
347 .ok_or_else(|| {
348 make_invariant_violation!(
349 "Failed to load type for event tag: {}",
350 tag
351 )
352 }),
353 )?;
354 let layout = self
355 .vm
356 .get_runtime()
357 .type_to_type_layout(&ty)
358 .map_err(|e| self.convert_vm_error(e))?;
359 let Some(bytes) = value.typed_serialize(&layout) else {
360 invariant_violation!("Failed to deserialize already serialized Move value");
361 };
362 Ok((module_id.clone(), tag, bytes))
363 })
364 .collect::<Result<Vec<_>, ExecutionError>>()?;
365 self.user_events.extend(new_events);
366 Ok(())
367 }
368
369 pub fn splat_args<Items: IntoIterator<Item = Argument>>(
375 &self,
376 start_idx: usize,
377 args: Items,
378 ) -> Result<Vec<Arg>, ExecutionError>
379 where
380 Items::IntoIter: ExactSizeIterator,
381 {
382 if !self.protocol_config.normalize_ptb_arguments() {
383 Ok(args.into_iter().map(|arg| Arg(Arg_::V1(arg))).collect())
384 } else {
385 let args = args.into_iter();
386 let _args_len = args.len();
387 let mut res = vec![];
388 for (arg_idx, arg) in args.enumerate() {
389 self.splat_arg(&mut res, arg)
390 .map_err(|e| e.into_execution_error(start_idx + arg_idx))?;
391 }
392 debug_assert_eq!(res.len(), _args_len);
393 Ok(res)
394 }
395 }
396
397 fn splat_arg(&self, res: &mut Vec<Arg>, arg: Argument) -> Result<(), EitherError> {
398 match arg {
399 Argument::GasCoin => res.push(Arg(Arg_::V2(NormalizedArg::GasCoin))),
400 Argument::Input(i) => {
401 if i as usize >= self.inputs.len() {
402 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
403 }
404 res.push(Arg(Arg_::V2(NormalizedArg::Input(i))))
405 }
406 Argument::NestedResult(i, j) => {
407 let Some(command_result) = self.results.get(i as usize) else {
408 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
409 };
410 if j as usize >= command_result.len() {
411 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
412 result_idx: i,
413 secondary_idx: j,
414 }
415 .into());
416 };
417 res.push(Arg(Arg_::V2(NormalizedArg::Result(i, j))))
418 }
419 Argument::Result(i) => {
420 let Some(result) = self.results.get(i as usize) else {
421 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
422 };
423 let Ok(len): Result<u16, _> = result.len().try_into() else {
424 invariant_violation!("Result of length greater than u16::MAX");
425 };
426 if len != 1 {
427 return Err(
429 CommandArgumentError::InvalidResultArity { result_idx: i }.into()
430 );
431 }
432 res.extend((0..len).map(|j| Arg(Arg_::V2(NormalizedArg::Result(i, j)))))
433 }
434 }
435 Ok(())
436 }
437
438 pub fn one_arg(
439 &self,
440 command_arg_idx: usize,
441 arg: Argument,
442 ) -> Result<Arg, ExecutionError> {
443 let args = self.splat_args(command_arg_idx, vec![arg])?;
444 let Ok([arg]): Result<[Arg; 1], _> = args.try_into() else {
445 return Err(command_argument_error(
446 CommandArgumentError::InvalidArgumentArity,
447 command_arg_idx,
448 ));
449 };
450 Ok(arg)
451 }
452
453 pub fn by_value_arg<V: TryFromValue>(
458 &mut self,
459 command_kind: CommandKind,
460 arg_idx: usize,
461 arg: Arg,
462 ) -> Result<V, ExecutionError> {
463 self.by_value_arg_(command_kind, arg)
464 .map_err(|e| e.into_execution_error(arg_idx))
465 }
466 fn by_value_arg_<V: TryFromValue>(
467 &mut self,
468 command_kind: CommandKind,
469 arg: Arg,
470 ) -> Result<V, EitherError> {
471 let shared_obj_deletion_enabled = self.protocol_config.shared_object_deletion();
472 let per_command_shared_object_transfer_rules = self
473 .protocol_config
474 .per_command_shared_object_transfer_rules();
475 let is_borrowed = self.arg_is_borrowed(&arg);
476 let (input_metadata_opt, val_opt, shared_object_ids) =
477 self.borrow_mut(arg, UsageKind::ByValue)?;
478 let is_copyable = if let Some(val) = val_opt {
479 val.is_copyable()
480 } else {
481 return Err(CommandArgumentError::InvalidValueUsage.into());
482 };
483 if !is_copyable && is_borrowed {
489 return Err(CommandArgumentError::InvalidValueUsage.into());
490 }
491 if arg.is_gas_coin() && !matches!(command_kind, CommandKind::TransferObjects) {
493 return Err(CommandArgumentError::InvalidGasCoinUsage.into());
494 }
495 if matches!(
497 input_metadata_opt,
498 Some(InputObjectMetadata::InputObject {
499 owner: Owner::Immutable,
500 ..
501 })
502 ) {
503 return Err(CommandArgumentError::InvalidObjectByValue.into());
504 }
505 if (
506 matches!(
508 input_metadata_opt,
509 Some(InputObjectMetadata::InputObject {
510 owner: Owner::Shared { .. },
511 ..
512 })
513 ) && !shared_obj_deletion_enabled
514 ) {
515 return Err(CommandArgumentError::InvalidObjectByValue.into());
516 }
517
518 match input_metadata_opt {
520 Some(InputObjectMetadata::InputObject { mutability, .. }) => match mutability {
521 Mutability::Mutable | Mutability::NonExclusiveWrite => (),
524 Mutability::Immutable => {
525 return Err(CommandArgumentError::InvalidObjectByValue.into());
526 }
527 },
528 Some(InputObjectMetadata::Receiving { .. }) => (),
529 None => (),
530 }
531
532 let val = if is_copyable {
533 val_opt.as_ref().unwrap().clone()
534 } else {
535 val_opt.take().unwrap()
536 };
537 if per_command_shared_object_transfer_rules {
538 let shared_object_ids = shared_object_ids.clone();
541 self.per_command_by_value_shared_objects
542 .extend(shared_object_ids);
543 }
544 Ok(V::try_from_value(val)?)
545 }
546
547 pub fn borrow_arg_mut<V: TryFromValue>(
553 &mut self,
554 arg_idx: usize,
555 arg: Arg,
556 ) -> Result<V, ExecutionError> {
557 self.borrow_arg_mut_(arg)
558 .map_err(|e| e.into_execution_error(arg_idx))
559 }
560 fn borrow_arg_mut_<V: TryFromValue>(&mut self, arg: Arg) -> Result<V, EitherError> {
561 let per_command_shared_object_transfer_rules = self
562 .protocol_config
563 .per_command_shared_object_transfer_rules();
564 if self.arg_is_borrowed(&arg) {
566 return Err(CommandArgumentError::InvalidValueUsage.into());
567 }
568 self.borrowed.insert(arg, true);
569 let (input_metadata_opt, val_opt, shared_object_ids) =
570 self.borrow_mut(arg, UsageKind::BorrowMut)?;
571 let is_copyable = if let Some(val) = val_opt {
572 val.is_copyable()
573 } else {
574 return Err(CommandArgumentError::InvalidValueUsage.into());
576 };
577 match input_metadata_opt {
578 Some(InputObjectMetadata::InputObject { mutability, .. }) => match mutability {
579 Mutability::Mutable | Mutability::NonExclusiveWrite => (),
580 Mutability::Immutable => {
581 return Err(CommandArgumentError::InvalidObjectByMutRef.into());
582 }
583 },
584 Some(InputObjectMetadata::Receiving { .. }) => (),
585 None => (),
586 }
587 let val = if is_copyable {
590 val_opt.as_ref().unwrap().clone()
591 } else {
592 val_opt.take().unwrap()
593 };
594 if per_command_shared_object_transfer_rules {
595 let normalized_arg = match arg {
598 Arg(Arg_::V2(arg)) => arg,
599 Arg(Arg_::V1(_)) => {
600 invariant_violation!(
601 "v1 should not be used with per_command_shared_object_transfer_rules"
602 )
603 }
604 };
605 match normalized_arg {
606 NormalizedArg::GasCoin | NormalizedArg::Input(_) => (),
607 NormalizedArg::Result(_, _) => {
608 let shared_object_ids = shared_object_ids.clone();
612 self.per_command_by_value_shared_objects
613 .extend(shared_object_ids);
614 }
615 }
616 }
617 Ok(V::try_from_value(val)?)
618 }
619
620 pub fn borrow_arg<V: TryFromValue>(
624 &mut self,
625 arg_idx: usize,
626 arg: Arg,
627 type_: &Type,
628 ) -> Result<V, ExecutionError> {
629 self.borrow_arg_(arg, type_)
630 .map_err(|e| e.into_execution_error(arg_idx))
631 }
632 fn borrow_arg_<V: TryFromValue>(
633 &mut self,
634 arg: Arg,
635 arg_type: &Type,
636 ) -> Result<V, EitherError> {
637 if self.arg_is_mut_borrowed(&arg) {
641 return Err(CommandArgumentError::InvalidValueUsage.into());
642 }
643 self.borrowed.insert(arg, false);
644 let (_input_metadata_opt, val_opt, _shared_object_ids) =
645 self.borrow_mut(arg, UsageKind::BorrowImm)?;
646 if val_opt.is_none() {
647 return Err(CommandArgumentError::InvalidValueUsage.into());
648 }
649
650 if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
652 let Type::Reference(inner) = arg_type else {
653 return Err(CommandArgumentError::InvalidValueUsage.into());
654 };
655 *recv_arg_type = Some(*(*inner).clone());
656 }
657
658 Ok(V::try_from_value(val_opt.as_ref().unwrap().clone())?)
659 }
660
661 pub fn restore_arg<Mode: ExecutionMode>(
663 &mut self,
664 updates: &mut Mode::ArgumentUpdates,
665 arg: Arg,
666 value: Value,
667 ) -> Result<(), ExecutionError> {
668 let per_command_shared_object_transfer_rules = self
669 .protocol_config
670 .per_command_shared_object_transfer_rules();
671 Mode::add_argument_update(self, updates, arg.into(), &value)?;
672 let was_mut_opt = self.borrowed.remove(&arg);
673 assert_invariant!(
674 was_mut_opt.is_some() && was_mut_opt.unwrap(),
675 "Should never restore a non-mut borrowed value. \
676 The take+restore is an implementation detail of mutable references"
677 );
678 let Ok((_, value_opt, shared_object_ids)) = self.borrow_mut_impl(arg, None) else {
680 invariant_violation!("Should be able to borrow argument to restore it")
681 };
682 if per_command_shared_object_transfer_rules {
683 let normalized_arg = match arg {
684 Arg(Arg_::V2(arg)) => arg,
685 Arg(Arg_::V1(_)) => {
686 invariant_violation!(
687 "v1 should not be used with per_command_shared_object_transfer_rules"
688 )
689 }
690 };
691 match normalized_arg {
692 NormalizedArg::GasCoin | NormalizedArg::Input(_) => (),
693 NormalizedArg::Result(_, _) => {
694 shared_object_ids.clear();
698 }
699 }
700 }
701
702 let old_value = value_opt.replace(value);
703 assert_invariant!(
704 old_value.is_none() || old_value.unwrap().is_copyable(),
705 "Should never restore a non-taken value, unless it is copyable. \
706 The take+restore is an implementation detail of mutable references"
707 );
708
709 Ok(())
710 }
711
712 pub fn transfer_object(
714 &mut self,
715 obj: ObjectValue,
716 addr: SuiAddress,
717 ) -> Result<(), ExecutionError> {
718 self.additional_transfers.push((addr, obj));
719 Ok(())
720 }
721
722 pub fn new_package<'p>(
724 &self,
725 modules: &[CompiledModule],
726 dependencies: impl IntoIterator<Item = &'p MovePackage>,
727 ) -> Result<MovePackage, ExecutionError> {
728 MovePackage::new_initial(modules, self.protocol_config, dependencies)
729 }
730
731 pub fn upgrade_package<'p>(
733 &self,
734 storage_id: ObjectID,
735 previous_package: &MovePackage,
736 new_modules: &[CompiledModule],
737 dependencies: impl IntoIterator<Item = &'p MovePackage>,
738 ) -> Result<MovePackage, ExecutionError> {
739 previous_package.new_upgraded(
740 storage_id,
741 new_modules,
742 self.protocol_config,
743 dependencies,
744 )
745 }
746
747 pub fn write_package(&mut self, package: MovePackage) {
749 self.new_packages.push(package);
750 }
751
752 pub fn pop_package(&mut self) -> Option<MovePackage> {
757 self.new_packages.pop()
758 }
759
760 pub fn push_command_results(
762 &mut self,
763 command_kind: CommandKind,
764 mut results: Vec<Value>,
765 ) -> Result<(), ExecutionError> {
766 assert_invariant!(
767 self.borrowed.values().all(|is_mut| !is_mut),
768 "all mut borrows should be restored"
769 );
770 self.borrowed = HashMap::new();
772 let results = if self
773 .protocol_config
774 .per_command_shared_object_transfer_rules()
775 {
776 if let CommandKind::MakeMoveVec = command_kind {
777 assert_invariant!(
779 results.len() == 1,
780 "MakeMoveVec should return a single result"
781 );
782 let mut result = ResultValue::new(results.pop().unwrap());
783 result.shared_object_ids =
784 std::mem::take(&mut self.per_command_by_value_shared_objects);
785 vec![result]
786 } else {
787 check_shared_object_usage(
790 self.object_runtime()?,
791 &self.per_command_by_value_shared_objects,
792 )?;
793 self.per_command_by_value_shared_objects.clear();
794 results.into_iter().map(ResultValue::new).collect()
795 }
796 } else {
797 results.into_iter().map(ResultValue::new).collect()
798 };
799 self.results.push(results);
800 Ok(())
801 }
802
803 pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
805 let Self {
806 protocol_config,
807 vm,
808 linkage_view,
809 mut native_extensions,
810 tx_context,
811 gas_charger,
812 additional_transfers,
813 new_packages,
814 gas,
815 inputs,
816 results,
817 user_events,
818 state_view,
819 ..
820 } = self;
821 let ref_context: &RefCell<TxContext> = tx_context.borrow();
822 let tx_digest = ref_context.borrow().digest();
823 let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
824 let mut loaded_runtime_objects = BTreeMap::new();
825 let mut additional_writes = BTreeMap::new();
826 let mut by_value_shared_objects = BTreeSet::new();
827 let mut consensus_owner_objects = BTreeMap::new();
828 for input in inputs.into_iter().chain(std::iter::once(gas)) {
829 let InputValue {
830 object_metadata:
831 Some(InputObjectMetadata::InputObject {
832 mutability: Mutability::Mutable | Mutability::NonExclusiveWrite,
839 id,
840 version,
841 owner,
842 }),
843 inner: ResultValue { value, .. },
844 } = input
845 else {
846 continue;
847 };
848 loaded_runtime_objects.insert(
849 id,
850 LoadedRuntimeObject {
851 version,
852 is_modified: true,
853 },
854 );
855 if let Some(Value::Object(object_value)) = value {
856 add_additional_write(&mut additional_writes, owner, object_value)?;
857 } else if owner.is_shared() {
858 by_value_shared_objects.insert(id);
859 } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
860 consensus_owner_objects.insert(id, owner.clone());
861 }
862 }
863 if !Mode::allow_arbitrary_values() {
866 for (i, command_result) in results.iter().enumerate() {
867 for (j, result_value) in command_result.iter().enumerate() {
868 let ResultValue {
869 last_usage_kind,
870 value,
871 shared_object_ids: _,
872 } = result_value;
873 match value {
874 None => (),
875 Some(Value::Object(_)) => {
876 return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
877 result_idx: i as u16,
878 secondary_idx: j as u16,
879 }
880 .into());
881 }
882 Some(Value::Raw(RawValueType::Any, _)) => (),
883 Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
884 if abilities.has_drop()
890 || (abilities.has_copy()
891 && matches!(last_usage_kind, Some(UsageKind::ByValue)))
892 {
893 } else {
894 let msg = if abilities.has_copy() {
895 "The value has copy, but not drop. \
896 Its last usage must be by-value so it can be taken."
897 } else {
898 "Unused value without drop"
899 };
900 return Err(ExecutionError::new_with_source(
901 ExecutionErrorKind::UnusedValueWithoutDrop {
902 result_idx: i as u16,
903 secondary_idx: j as u16,
904 },
905 msg,
906 ));
907 }
908 }
909 Some(Value::Receiving(_, _, _)) => (),
911 }
912 }
913 }
914 }
915 for (recipient, object_value) in additional_transfers {
917 let owner = Owner::AddressOwner(recipient);
918 add_additional_write(&mut additional_writes, owner, object_value)?;
919 }
920 if let Some(gas_id) = gas_id_opt {
922 refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
923 }
924
925 let object_runtime: ObjectRuntime = native_extensions.remove().map_err(|e| {
926 convert_vm_error(
927 e.finish(Location::Undefined),
928 vm,
929 &linkage_view,
930 protocol_config.resolve_abort_locations_to_package_id(),
931 )
932 })?;
933
934 let RuntimeResults {
935 writes,
936 user_events: remaining_events,
937 accumulator_events,
938 loaded_child_objects,
939 mut created_object_ids,
940 deleted_object_ids,
941 settlement_input_sui,
942 settlement_output_sui,
943 } = object_runtime.finish()?;
944 assert_invariant!(
945 remaining_events.is_empty(),
946 "Events should be taken after every Move call"
947 );
948
949 loaded_runtime_objects.extend(loaded_child_objects);
950
951 let mut written_objects = BTreeMap::new();
952 for package in new_packages {
953 let package_obj = Object::new_from_package(package, tx_digest);
954 let id = package_obj.id();
955 created_object_ids.insert(id);
956 written_objects.insert(id, package_obj);
957 }
958 for (id, additional_write) in additional_writes {
959 let AdditionalWrite {
960 recipient,
961 type_,
962 has_public_transfer,
963 bytes,
964 } = additional_write;
965 let move_object = unsafe {
967 create_written_object::<Mode>(
968 vm,
969 &linkage_view,
970 protocol_config,
971 &loaded_runtime_objects,
972 id,
973 type_,
974 has_public_transfer,
975 bytes,
976 )?
977 };
978 let object = Object::new_move(move_object, recipient, tx_digest);
979 written_objects.insert(id, object);
980 if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
981 loaded.is_modified = true;
982 }
983 }
984
985 for (id, (recipient, tag, value)) in writes {
986 let ty = unwrap_type_tag_load(
987 protocol_config,
988 vm.get_runtime()
989 .try_load_cached_type(&TypeTag::from(tag.clone()))
990 .map_err(|e| {
991 convert_vm_error(
992 e,
993 vm,
994 &linkage_view,
995 protocol_config.resolve_abort_locations_to_package_id(),
996 )
997 })?
998 .ok_or_else(|| {
999 make_invariant_violation!("Failed to load type for event tag: {}", tag)
1000 }),
1001 )?;
1002 let abilities = vm.get_runtime().get_type_abilities(&ty).map_err(|e| {
1003 convert_vm_error(
1004 e,
1005 vm,
1006 &linkage_view,
1007 protocol_config.resolve_abort_locations_to_package_id(),
1008 )
1009 })?;
1010 let has_public_transfer = abilities.has_store();
1011 let layout = vm.get_runtime().type_to_type_layout(&ty).map_err(|e| {
1012 convert_vm_error(
1013 e,
1014 vm,
1015 &linkage_view,
1016 protocol_config.resolve_abort_locations_to_package_id(),
1017 )
1018 })?;
1019 let Some(bytes) = value.typed_serialize(&layout) else {
1020 invariant_violation!("Failed to deserialize already serialized Move value");
1021 };
1022 let move_object = unsafe {
1024 create_written_object::<Mode>(
1025 vm,
1026 &linkage_view,
1027 protocol_config,
1028 &loaded_runtime_objects,
1029 id,
1030 ty,
1031 has_public_transfer,
1032 bytes,
1033 )?
1034 };
1035 let object = Object::new_move(move_object, recipient, tx_digest);
1036 written_objects.insert(id, object);
1037 }
1038
1039 finish(
1040 protocol_config,
1041 state_view,
1042 gas_charger,
1043 &ref_context.borrow(),
1044 &by_value_shared_objects,
1045 &consensus_owner_objects,
1046 loaded_runtime_objects,
1047 written_objects,
1048 created_object_ids,
1049 deleted_object_ids,
1050 user_events,
1051 accumulator_events,
1052 settlement_input_sui,
1053 settlement_output_sui,
1054 )
1055 }
1056
1057 pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
1059 convert_vm_error(
1060 error,
1061 self.vm,
1062 &self.linkage_view,
1063 self.protocol_config.resolve_abort_locations_to_package_id(),
1064 )
1065 }
1066
1067 pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
1069 convert_type_argument_error(
1070 idx,
1071 error,
1072 self.vm,
1073 &self.linkage_view,
1074 self.protocol_config.resolve_abort_locations_to_package_id(),
1075 )
1076 }
1077
1078 fn arg_is_borrowed(&self, arg: &Arg) -> bool {
1080 self.borrowed.contains_key(arg)
1081 }
1082
1083 fn arg_is_mut_borrowed(&self, arg: &Arg) -> bool {
1085 matches!(self.borrowed.get(arg), Some(true))
1086 }
1087
1088 fn borrow_mut(
1090 &mut self,
1091 arg: Arg,
1092 usage: UsageKind,
1093 ) -> Result<
1094 (
1095 Option<&InputObjectMetadata>,
1096 &mut Option<Value>,
1097 &mut BTreeSet<ObjectID>,
1098 ),
1099 EitherError,
1100 > {
1101 self.borrow_mut_impl(arg, Some(usage))
1102 }
1103
1104 fn borrow_mut_impl(
1107 &mut self,
1108 arg: Arg,
1109 update_last_usage: Option<UsageKind>,
1110 ) -> Result<
1111 (
1112 Option<&InputObjectMetadata>,
1113 &mut Option<Value>,
1114 &mut BTreeSet<ObjectID>,
1115 ),
1116 EitherError,
1117 > {
1118 match arg.0 {
1119 Arg_::V1(arg) => {
1120 assert_invariant!(
1121 !self.protocol_config.normalize_ptb_arguments(),
1122 "Should not be using v1 args with normalized args"
1123 );
1124 Ok(self.borrow_mut_impl_v1(arg, update_last_usage)?)
1125 }
1126 Arg_::V2(arg) => {
1127 assert_invariant!(
1128 self.protocol_config.normalize_ptb_arguments(),
1129 "Should be using only v2 args with normalized args"
1130 );
1131 Ok(self.borrow_mut_impl_v2(arg, update_last_usage)?)
1132 }
1133 }
1134 }
1135
1136 fn borrow_mut_impl_v1(
1138 &mut self,
1139 arg: Argument,
1140 update_last_usage: Option<UsageKind>,
1141 ) -> Result<
1142 (
1143 Option<&InputObjectMetadata>,
1144 &mut Option<Value>,
1145 &mut BTreeSet<ObjectID>,
1146 ),
1147 CommandArgumentError,
1148 > {
1149 let (metadata, result_value) = match arg {
1150 Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1151 Argument::Input(i) => {
1152 let Some(input_value) = self.inputs.get_mut(i as usize) else {
1153 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1154 };
1155 (input_value.object_metadata.as_ref(), &mut input_value.inner)
1156 }
1157 Argument::Result(i) => {
1158 let Some(command_result) = self.results.get_mut(i as usize) else {
1159 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1160 };
1161 if command_result.len() != 1 {
1162 return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
1163 }
1164 (None, &mut command_result[0])
1165 }
1166 Argument::NestedResult(i, j) => {
1167 let Some(command_result) = self.results.get_mut(i as usize) else {
1168 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1169 };
1170 let Some(result_value) = command_result.get_mut(j as usize) else {
1171 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
1172 result_idx: i,
1173 secondary_idx: j,
1174 });
1175 };
1176 (None, result_value)
1177 }
1178 };
1179 if let Some(usage) = update_last_usage {
1180 result_value.last_usage_kind = Some(usage);
1181 }
1182 Ok((
1183 metadata,
1184 &mut result_value.value,
1185 &mut result_value.shared_object_ids,
1186 ))
1187 }
1188
1189 fn borrow_mut_impl_v2(
1191 &mut self,
1192 arg: NormalizedArg,
1193 update_last_usage: Option<UsageKind>,
1194 ) -> Result<
1195 (
1196 Option<&InputObjectMetadata>,
1197 &mut Option<Value>,
1198 &mut BTreeSet<ObjectID>,
1199 ),
1200 ExecutionError,
1201 > {
1202 let (metadata, result_value) = match arg {
1203 NormalizedArg::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1204 NormalizedArg::Input(i) => {
1205 let input_value = self
1206 .inputs
1207 .get_mut(i as usize)
1208 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1209 let metadata = input_value.object_metadata.as_ref();
1210 (metadata, &mut input_value.inner)
1211 }
1212 NormalizedArg::Result(i, j) => {
1213 let result_value = self
1214 .results
1215 .get_mut(i as usize)
1216 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?
1217 .get_mut(j as usize)
1218 .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1219 (None, result_value)
1220 }
1221 };
1222 if let Some(usage) = update_last_usage {
1223 result_value.last_usage_kind = Some(usage);
1224 }
1225 Ok((
1226 metadata,
1227 &mut result_value.value,
1228 &mut result_value.shared_object_ids,
1229 ))
1230 }
1231
1232 pub(crate) fn execute_function_bypass_visibility(
1233 &mut self,
1234 module: &ModuleId,
1235 function_name: &IdentStr,
1236 ty_args: Vec<Type>,
1237 args: Vec<impl Borrow<[u8]>>,
1238 tracer: &mut Option<MoveTraceBuilder>,
1239 ) -> VMResult<SerializedReturnValues> {
1240 let gas_status = self.gas_charger.move_gas_status_mut();
1241 let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
1242 self.vm.get_runtime().execute_function_bypass_visibility(
1243 module,
1244 function_name,
1245 ty_args,
1246 args,
1247 &mut data_store,
1248 &mut SuiGasMeter(gas_status),
1249 &mut self.native_extensions,
1250 tracer.as_mut(),
1251 )
1252 }
1253
1254 pub(crate) fn load_function(
1255 &mut self,
1256 module_id: &ModuleId,
1257 function_name: &IdentStr,
1258 type_arguments: &[Type],
1259 ) -> VMResult<LoadedFunctionInstantiation> {
1260 let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
1261 self.vm.get_runtime().load_function(
1262 module_id,
1263 function_name,
1264 type_arguments,
1265 &mut data_store,
1266 )
1267 }
1268
1269 pub(crate) fn make_object_value(
1270 &mut self,
1271 type_: MoveObjectType,
1272 has_public_transfer: bool,
1273 used_in_non_entry_move_call: bool,
1274 contents: &[u8],
1275 ) -> Result<ObjectValue, ExecutionError> {
1276 make_object_value(
1277 self.protocol_config,
1278 self.vm,
1279 &mut self.linkage_view,
1280 &self.new_packages,
1281 type_,
1282 has_public_transfer,
1283 used_in_non_entry_move_call,
1284 contents,
1285 )
1286 }
1287
1288 pub fn publish_module_bundle(
1289 &mut self,
1290 modules: Vec<Vec<u8>>,
1291 sender: AccountAddress,
1292 ) -> VMResult<()> {
1293 let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
1296 self.vm.get_runtime().publish_module_bundle(
1297 modules,
1298 sender,
1299 &mut data_store,
1300 &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
1301 )
1302 }
1303
1304 pub fn size_bound_raw(&self, bound: u64) -> SizeBound {
1305 if self.protocol_config.max_ptb_value_size_v2() {
1306 SizeBound::Raw(bound)
1307 } else {
1308 SizeBound::Object(bound)
1309 }
1310 }
1311
1312 pub fn size_bound_vector_elem(&self, bound: u64) -> SizeBound {
1313 if self.protocol_config.max_ptb_value_size_v2() {
1314 SizeBound::VectorElem(bound)
1315 } else {
1316 SizeBound::Object(bound)
1317 }
1318 }
1319
1320 pub(crate) fn deserialize_modules(
1321 &self,
1322 module_bytes: &[Vec<u8>],
1323 ) -> Result<Vec<CompiledModule>, ExecutionError> {
1324 let binary_config = self.protocol_config.binary_config(None);
1325 let modules = module_bytes
1326 .iter()
1327 .map(|b| {
1328 CompiledModule::deserialize_with_config(b, &binary_config)
1329 .map_err(|e| e.finish(Location::Undefined))
1330 })
1331 .collect::<VMResult<Vec<CompiledModule>>>()
1332 .map_err(|e| self.convert_vm_error(e))?;
1333
1334 assert_invariant!(
1335 !modules.is_empty(),
1336 "input checker ensures package is not empty"
1337 );
1338
1339 Ok(modules)
1340 }
1341 }
1342
1343 impl Arg {
1344 fn is_gas_coin(&self) -> bool {
1345 match self {
1347 Arg(Arg_::V1(a)) => matches!(a, Argument::GasCoin),
1348 Arg(Arg_::V2(n)) => matches!(n, NormalizedArg::GasCoin),
1349 }
1350 }
1351 }
1352
1353 impl From<Arg> for Argument {
1354 fn from(arg: Arg) -> Self {
1355 match arg.0 {
1356 Arg_::V1(a) => a,
1357 Arg_::V2(normalized) => match normalized {
1358 NormalizedArg::GasCoin => Argument::GasCoin,
1359 NormalizedArg::Input(i) => Argument::Input(i),
1360 NormalizedArg::Result(i, j) => Argument::NestedResult(i, j),
1361 },
1362 }
1363 }
1364 }
1365
1366 impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
1367 fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
1368 self.vm
1369 .get_runtime()
1370 .get_type_tag(type_)
1371 .map_err(|e| self.convert_vm_error(e))
1372 }
1373 }
1374
1375 fn get_package(
1378 package_store: &dyn PackageStore,
1379 package_id: ObjectID,
1380 ) -> VMResult<Rc<MovePackage>> {
1381 match package_store.get_package(&package_id) {
1382 Ok(Some(package)) => Ok(package),
1383 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1384 .with_message(format!("Cannot find link context {package_id} in store"))
1385 .finish(Location::Undefined)),
1386 Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1387 .with_message(format!("Error loading {package_id} from store: {err}"))
1388 .finish(Location::Undefined)),
1389 }
1390 }
1391
1392 pub fn check_shared_object_usage<'a>(
1394 object_runtime: &ObjectRuntime,
1395 consumed_shared_objects: impl IntoIterator<Item = &'a ObjectID>,
1396 ) -> Result<(), ExecutionError> {
1397 for id in consumed_shared_objects {
1398 let is_valid_usage = object_runtime.is_deleted(id)
1400 || matches!(
1401 object_runtime.is_transferred(id),
1402 Some(Owner::Shared { .. })
1403 );
1404 if !is_valid_usage {
1405 return Err(ExecutionError::new_with_source(
1406 ExecutionErrorKind::SharedObjectOperationNotAllowed,
1407 format!(
1408 "Shared object operation on {} not allowed: \
1409 cannot be frozen, transferred, or wrapped",
1410 id
1411 ),
1412 ));
1413 }
1414 }
1415 Ok(())
1416 }
1417
1418 pub fn finish(
1419 protocol_config: &ProtocolConfig,
1420 state_view: &dyn ExecutionState,
1421 gas_charger: &mut GasCharger,
1422 tx_context: &TxContext,
1423 by_value_shared_objects: &BTreeSet<ObjectID>,
1424 consensus_owner_objects: &BTreeMap<ObjectID, Owner>,
1425 loaded_runtime_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
1426 written_objects: BTreeMap<ObjectID, Object>,
1427 created_object_ids: IndexSet<ObjectID>,
1428 deleted_object_ids: IndexSet<ObjectID>,
1429 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
1430 accumulator_events: Vec<MoveAccumulatorEvent>,
1431 settlement_input_sui: u64,
1432 settlement_output_sui: u64,
1433 ) -> Result<ExecutionResults, ExecutionError> {
1434 for id in by_value_shared_objects {
1439 if let Some(obj) = written_objects.get(id) {
1442 if !obj.is_shared() {
1443 if protocol_config.per_command_shared_object_transfer_rules() {
1444 invariant_violation!(
1445 "There should be no shared objects unaccounted for when \
1446 per_command_shared_object_transfer_rules is enabled"
1447 )
1448 } else {
1449 return Err(ExecutionError::new(
1450 ExecutionErrorKind::SharedObjectOperationNotAllowed,
1451 Some(
1452 format!(
1453 "Shared object operation on {} not allowed: \
1454 cannot be frozen, transferred, or wrapped",
1455 id
1456 )
1457 .into(),
1458 ),
1459 ));
1460 }
1461 }
1462 } else {
1463 if !deleted_object_ids.contains(id) {
1466 if protocol_config.per_command_shared_object_transfer_rules() {
1467 invariant_violation!(
1468 "There should be no shared objects unaccounted for when \
1469 per_command_shared_object_transfer_rules is enabled"
1470 )
1471 } else {
1472 return Err(ExecutionError::new(
1473 ExecutionErrorKind::SharedObjectOperationNotAllowed,
1474 Some(
1475 format!("Shared object operation on {} not allowed: \
1476 shared objects used by value must be re-shared if not deleted", id).into(),
1477 ),
1478 ));
1479 }
1480 }
1481 }
1482 }
1483
1484 for (id, original_owner) in consensus_owner_objects {
1486 let Owner::ConsensusAddressOwner { owner, .. } = original_owner else {
1487 panic!(
1488 "verified before adding to `consensus_owner_objects` that these are ConsensusAddressOwner"
1489 );
1490 };
1491 if tx_context.sender() != *owner {
1494 debug_fatal!(
1495 "transaction with a singly owned input object where the tx sender is not the owner should never be executed"
1496 );
1497 if protocol_config.per_command_shared_object_transfer_rules() {
1498 invariant_violation!(
1499 "Shared object operation on {} not allowed: \
1500 transaction with singly owned input object must be sent by the owner",
1501 id,
1502 );
1503 } else {
1504 return Err(ExecutionError::new(
1505 ExecutionErrorKind::SharedObjectOperationNotAllowed,
1506 Some(
1507 format!("Shared object operation on {} not allowed: \
1508 transaction with singly owned input object must be sent by the owner", id).into(),
1509 ),
1510 ));
1511 }
1512 }
1513 }
1521
1522 let user_events: Vec<Event> = user_events
1523 .into_iter()
1524 .map(|(module_id, tag, contents)| {
1525 Event::new(
1526 module_id.address(),
1527 module_id.name(),
1528 tx_context.sender(),
1529 tag,
1530 contents,
1531 )
1532 })
1533 .collect();
1534
1535 let mut receiving_funds_type_and_owners = BTreeMap::new();
1536 let accumulator_events = accumulator_events
1537 .into_iter()
1538 .map(|accum_event| {
1539 if let Some(ty) = Balance::maybe_get_balance_type_param(&accum_event.target_ty) {
1540 receiving_funds_type_and_owners
1541 .entry(ty)
1542 .or_insert_with(BTreeSet::new)
1543 .insert(accum_event.target_addr.into());
1544 }
1545 let value = match accum_event.value {
1546 MoveAccumulatorValue::U64(amount) => AccumulatorValue::Integer(amount),
1547 MoveAccumulatorValue::EventRef(event_idx) => {
1548 let Some(event) = user_events.get(event_idx as usize) else {
1549 invariant_violation!(
1550 "Could not find authenticated event at index {}",
1551 event_idx
1552 );
1553 };
1554 let digest = event.digest();
1555 AccumulatorValue::EventDigest(nonempty![(event_idx, digest)])
1556 }
1557 };
1558
1559 let address =
1560 AccumulatorAddress::new(accum_event.target_addr.into(), accum_event.target_ty);
1561
1562 let write = AccumulatorWriteV1 {
1563 address,
1564 operation: accum_event.action.into_sui_accumulator_action(),
1565 value,
1566 };
1567
1568 Ok(AccumulatorEvent::new(
1569 AccumulatorObjId::new_unchecked(accum_event.accumulator_id),
1570 write,
1571 ))
1572 })
1573 .collect::<Result<Vec<_>, ExecutionError>>()?;
1574
1575 if protocol_config.enable_coin_deny_list_v2() {
1576 for object in written_objects.values() {
1577 let coin_type = object.type_().and_then(|ty| ty.coin_type_maybe());
1578 let owner = object.owner.get_address_owner_address();
1579 if let (Some(ty), Ok(owner)) = (coin_type, owner) {
1580 receiving_funds_type_and_owners
1581 .entry(ty)
1582 .or_insert_with(BTreeSet::new)
1583 .insert(owner);
1584 }
1585 }
1586 let DenyListResult {
1587 result,
1588 num_non_gas_coin_owners,
1589 } = state_view.check_coin_deny_list(receiving_funds_type_and_owners);
1590 gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
1591 result?;
1592 }
1593
1594 Ok(ExecutionResults::V2(ExecutionResultsV2 {
1595 written_objects,
1596 modified_objects: loaded_runtime_objects
1597 .into_iter()
1598 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
1599 .collect(),
1600 created_object_ids: created_object_ids.into_iter().collect(),
1601 deleted_object_ids: deleted_object_ids.into_iter().collect(),
1602 user_events,
1603 accumulator_events,
1604 settlement_input_sui,
1605 settlement_output_sui,
1606 }))
1607 }
1608
1609 pub fn load_type_from_struct(
1610 vm: &MoveVM,
1611 linkage_view: &LinkageView,
1612 new_packages: &[MovePackage],
1613 struct_tag: &StructTag,
1614 ) -> VMResult<Type> {
1615 fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1616 Err(PartialVMError::new(code).finish(Location::Undefined))
1617 }
1618
1619 let StructTag {
1620 address,
1621 module,
1622 name,
1623 type_params,
1624 } = struct_tag;
1625
1626 let defining_id = ObjectID::from_address(*address);
1628
1629 let data_store = SuiDataStore::new(linkage_view, new_packages);
1630 let move_package = get_package(&data_store, defining_id)?;
1631
1632 let original_address = linkage_view.set_linkage(&move_package).map_err(|e| {
1635 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1636 .with_message(e.to_string())
1637 .finish(Location::Undefined)
1638 })?;
1639
1640 let runtime_id = ModuleId::new(original_address, module.clone());
1641 let data_store = SuiDataStore::new(linkage_view, new_packages);
1642 let res = vm.get_runtime().load_type(&runtime_id, name, &data_store);
1643 linkage_view.reset_linkage().map_err(|e| {
1644 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1645 .with_message(e.to_string())
1646 .finish(Location::Undefined)
1647 })?;
1648 let (idx, struct_type) = res?;
1649
1650 let type_param_constraints = struct_type.type_param_constraints();
1652 if type_param_constraints.len() != type_params.len() {
1653 return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1654 }
1655
1656 if type_params.is_empty() {
1657 Ok(Type::Datatype(idx))
1658 } else {
1659 let loaded_type_params = type_params
1660 .iter()
1661 .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1662 .collect::<VMResult<Vec<_>>>()?;
1663
1664 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1666 let abilities = vm.get_runtime().get_type_abilities(param)?;
1667 if !constraint.is_subset(abilities) {
1668 return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1669 }
1670 }
1671
1672 Ok(Type::DatatypeInstantiation(Box::new((
1673 idx,
1674 loaded_type_params,
1675 ))))
1676 }
1677 }
1678
1679 pub fn load_type(
1682 vm: &MoveVM,
1683 linkage_view: &LinkageView,
1684 new_packages: &[MovePackage],
1685 type_tag: &TypeTag,
1686 ) -> VMResult<Type> {
1687 Ok(match type_tag {
1688 TypeTag::Bool => Type::Bool,
1689 TypeTag::U8 => Type::U8,
1690 TypeTag::U16 => Type::U16,
1691 TypeTag::U32 => Type::U32,
1692 TypeTag::U64 => Type::U64,
1693 TypeTag::U128 => Type::U128,
1694 TypeTag::U256 => Type::U256,
1695 TypeTag::Address => Type::Address,
1696 TypeTag::Signer => Type::Signer,
1697
1698 TypeTag::Vector(inner) => {
1699 Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1700 }
1701 TypeTag::Struct(struct_tag) => {
1702 return load_type_from_struct(vm, linkage_view, new_packages, struct_tag);
1703 }
1704 })
1705 }
1706
1707 pub(crate) fn make_object_value(
1708 protocol_config: &ProtocolConfig,
1709 vm: &MoveVM,
1710 linkage_view: &mut LinkageView,
1711 new_packages: &[MovePackage],
1712 type_: MoveObjectType,
1713 has_public_transfer: bool,
1714 used_in_non_entry_move_call: bool,
1715 contents: &[u8],
1716 ) -> Result<ObjectValue, ExecutionError> {
1717 let contents = if type_.is_coin() {
1718 let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1719 invariant_violation!("Could not deserialize a coin")
1720 };
1721 ObjectContents::Coin(coin)
1722 } else {
1723 ObjectContents::Raw(contents.to_vec())
1724 };
1725
1726 let tag: StructTag = type_.into();
1727 let type_ = load_type_from_struct(vm, linkage_view, new_packages, &tag).map_err(|e| {
1728 convert_vm_error(
1729 e,
1730 vm,
1731 linkage_view,
1732 protocol_config.resolve_abort_locations_to_package_id(),
1733 )
1734 })?;
1735 let has_public_transfer = if protocol_config.recompute_has_public_transfer_in_execution() {
1736 let abilities = vm.get_runtime().get_type_abilities(&type_).map_err(|e| {
1737 convert_vm_error(
1738 e,
1739 vm,
1740 linkage_view,
1741 protocol_config.resolve_abort_locations_to_package_id(),
1742 )
1743 })?;
1744 abilities.has_store()
1745 } else {
1746 has_public_transfer
1747 };
1748 Ok(ObjectValue {
1749 type_,
1750 has_public_transfer,
1751 used_in_non_entry_move_call,
1752 contents,
1753 })
1754 }
1755
1756 pub(crate) fn value_from_object(
1757 protocol_config: &ProtocolConfig,
1758 vm: &MoveVM,
1759 linkage_view: &mut LinkageView,
1760 new_packages: &[MovePackage],
1761 object: &Object,
1762 ) -> Result<ObjectValue, ExecutionError> {
1763 let ObjectInner {
1764 data: Data::Move(object),
1765 ..
1766 } = object.as_inner()
1767 else {
1768 invariant_violation!("Expected a Move object");
1769 };
1770
1771 let used_in_non_entry_move_call = false;
1772 make_object_value(
1773 protocol_config,
1774 vm,
1775 linkage_view,
1776 new_packages,
1777 object.type_().clone(),
1778 object.has_public_transfer(),
1779 used_in_non_entry_move_call,
1780 object.contents(),
1781 )
1782 }
1783
1784 fn load_object(
1786 protocol_config: &ProtocolConfig,
1787 vm: &MoveVM,
1788 state_view: &dyn ExecutionState,
1789 linkage_view: &mut LinkageView,
1790 new_packages: &[MovePackage],
1791 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1792 mutability_override: Option<Mutability>,
1793 id: ObjectID,
1794 ) -> Result<InputValue, ExecutionError> {
1795 let Some(obj) = state_view.read_object(&id) else {
1796 invariant_violation!("Object {} does not exist yet", id);
1798 };
1799 assert_invariant!(
1801 mutability_override.is_none()
1802 || matches!(
1803 obj.owner,
1804 Owner::Shared { .. } | Owner::ConsensusAddressOwner { .. }
1805 ),
1806 "override_as_immutable should only be set for consensus objects"
1807 );
1808 let mutability = match obj.owner {
1809 Owner::AddressOwner(_) => Mutability::Mutable,
1810 Owner::Shared { .. } | Owner::ConsensusAddressOwner { .. } => {
1811 mutability_override.unwrap_or(Mutability::Mutable)
1812 }
1813 Owner::Immutable => Mutability::Immutable,
1814 Owner::ObjectOwner(_) => {
1815 invariant_violation!("ObjectOwner objects cannot be input")
1817 }
1818 };
1819 let owner = obj.owner.clone();
1820 let version = obj.version();
1821 let object_metadata = InputObjectMetadata::InputObject {
1822 id,
1823 mutability,
1824 owner: owner.clone(),
1825 version,
1826 };
1827 let obj_value = value_from_object(protocol_config, vm, linkage_view, new_packages, obj)?;
1828 let contained_uids = {
1829 let fully_annotated_layout = vm
1830 .get_runtime()
1831 .type_to_fully_annotated_layout(&obj_value.type_)
1832 .map_err(|e| {
1833 convert_vm_error(
1834 e,
1835 vm,
1836 linkage_view,
1837 protocol_config.resolve_abort_locations_to_package_id(),
1838 )
1839 })?;
1840 let mut bytes = vec![];
1841 obj_value.write_bcs_bytes(&mut bytes, None)?;
1842 match get_all_uids(&fully_annotated_layout, &bytes) {
1843 Err(e) => {
1844 invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1845 }
1846 Ok(uids) => uids,
1847 }
1848 };
1849 let runtime_input = object_runtime::InputObject {
1850 contained_uids,
1851 owner,
1852 version,
1853 };
1854 let prev = input_object_map.insert(id, runtime_input);
1855 assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1857 Ok(InputValue::new_object(object_metadata, obj_value))
1858 }
1859
1860 fn load_call_arg(
1862 protocol_config: &ProtocolConfig,
1863 vm: &MoveVM,
1864 state_view: &dyn ExecutionState,
1865 linkage_view: &mut LinkageView,
1866 new_packages: &[MovePackage],
1867 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1868 tx_context: &TxContext,
1869 call_arg: CallArg,
1870 ) -> Result<InputValue, ExecutionError> {
1871 Ok(match call_arg {
1872 CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1873 CallArg::Object(obj_arg) => load_object_arg(
1874 protocol_config,
1875 vm,
1876 state_view,
1877 linkage_view,
1878 new_packages,
1879 input_object_map,
1880 obj_arg,
1881 )?,
1882 CallArg::FundsWithdrawal(FundsWithdrawalArg {
1883 reservation,
1884 type_arg,
1885 withdraw_from,
1886 }) => {
1887 let Ok(type_arg) = type_arg.to_type_tag() else {
1888 invariant_violation!(
1890 "FundsWithdrawArg type arg should have been \
1891 checked at signing"
1892 );
1893 };
1894 let withdrawal_ty = Withdrawal::type_tag(type_arg);
1895 let ty =
1896 load_type(vm, linkage_view, new_packages, &withdrawal_ty).map_err(|e| {
1897 convert_type_argument_error(
1898 0,
1899 e,
1900 vm,
1901 linkage_view,
1902 protocol_config.resolve_abort_locations_to_package_id(),
1903 )
1904 })?;
1905 let abilities = vm.get_runtime().get_type_abilities(&ty).map_err(|e| {
1906 convert_vm_error(
1907 e,
1908 vm,
1909 linkage_view,
1910 protocol_config.resolve_abort_locations_to_package_id(),
1911 )
1912 })?;
1913 let loaded_ty = RawValueType::Loaded {
1914 ty,
1915 abilities,
1916 used_in_non_entry_move_call: false,
1917 };
1918 let owner = match withdraw_from {
1919 WithdrawFrom::Sender => tx_context.sender(),
1920 WithdrawFrom::Sponsor => {
1921 invariant_violation!(
1922 "WithdrawFrom::Sponsor call arg not supported, \
1923 should have been checked at signing"
1924 );
1925 }
1926 };
1927 debug_assert!({
1930 !abilities.has_key()
1931 && !abilities.has_store()
1932 && !abilities.has_copy()
1933 && abilities.has_drop()
1934 });
1935 let limit = match reservation {
1936 sui_types::transaction::Reservation::EntireBalance => {
1937 todo!("Entire balance withdrawal not yet supported")
1939 }
1940 sui_types::transaction::Reservation::MaxAmountU64(u) => U256::from(u),
1941 };
1942 InputValue::withdrawal(loaded_ty, owner, limit)
1943 }
1944 })
1945 }
1946
1947 fn load_object_arg(
1949 protocol_config: &ProtocolConfig,
1950 vm: &MoveVM,
1951 state_view: &dyn ExecutionState,
1952 linkage_view: &mut LinkageView,
1953 new_packages: &[MovePackage],
1954 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1955 obj_arg: ObjectArg,
1956 ) -> Result<InputValue, ExecutionError> {
1957 match obj_arg {
1958 ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1959 protocol_config,
1960 vm,
1961 state_view,
1962 linkage_view,
1963 new_packages,
1964 input_object_map,
1965 None,
1966 id,
1967 ),
1968 ObjectArg::SharedObject { id, mutability, .. } => {
1969 let mutability = match mutability {
1970 SharedObjectMutability::Mutable => Mutability::Mutable,
1971 SharedObjectMutability::NonExclusiveWrite => Mutability::NonExclusiveWrite,
1975 SharedObjectMutability::Immutable => Mutability::Immutable,
1976 };
1977 load_object(
1978 protocol_config,
1979 vm,
1980 state_view,
1981 linkage_view,
1982 new_packages,
1983 input_object_map,
1984 Some(mutability),
1985 id,
1986 )
1987 }
1988 ObjectArg::Receiving((id, version, _)) => {
1989 Ok(InputValue::new_receiving_object(id, version))
1990 }
1991 }
1992 }
1993
1994 fn add_additional_write(
1996 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1997 owner: Owner,
1998 object_value: ObjectValue,
1999 ) -> Result<(), ExecutionError> {
2000 let ObjectValue {
2001 type_,
2002 has_public_transfer,
2003 contents,
2004 ..
2005 } = object_value;
2006 let bytes = match contents {
2007 ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
2008 ObjectContents::Raw(bytes) => bytes,
2009 };
2010 let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
2011 ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
2012 })?;
2013 let additional_write = AdditionalWrite {
2014 recipient: owner,
2015 type_,
2016 has_public_transfer,
2017 bytes,
2018 };
2019 additional_writes.insert(object_id, additional_write);
2020 Ok(())
2021 }
2022
2023 fn refund_max_gas_budget(
2026 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
2027 gas_charger: &mut GasCharger,
2028 gas_id: ObjectID,
2029 ) -> Result<(), ExecutionError> {
2030 let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
2031 invariant_violation!("Gas object cannot be wrapped or destroyed")
2032 };
2033 let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
2034 invariant_violation!("Gas object must be a coin")
2035 };
2036 let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
2037 return Err(ExecutionError::new_with_source(
2038 ExecutionErrorKind::CoinBalanceOverflow,
2039 "Gas coin too large after returning the max gas budget",
2040 ));
2041 };
2042 coin.balance = Balance::new(new_balance);
2043 *bytes = coin.to_bcs_bytes();
2044 Ok(())
2045 }
2046
2047 unsafe fn create_written_object<Mode: ExecutionMode>(
2053 vm: &MoveVM,
2054 linkage_view: &LinkageView,
2055 protocol_config: &ProtocolConfig,
2056 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
2057 id: ObjectID,
2058 type_: Type,
2059 has_public_transfer: bool,
2060 contents: Vec<u8>,
2061 ) -> Result<MoveObject, ExecutionError> {
2062 debug_assert_eq!(
2063 id,
2064 MoveObject::id_opt(&contents).expect("object contents should start with an id")
2065 );
2066 let old_obj_ver = objects_modified_at
2067 .get(&id)
2068 .map(|obj: &LoadedRuntimeObject| obj.version);
2069
2070 let type_tag = vm.get_runtime().get_type_tag(&type_).map_err(|e| {
2071 convert_vm_error(
2072 e,
2073 vm,
2074 linkage_view,
2075 protocol_config.resolve_abort_locations_to_package_id(),
2076 )
2077 })?;
2078
2079 let struct_tag = match type_tag {
2080 TypeTag::Struct(inner) => *inner,
2081 _ => invariant_violation!("Non struct type for object"),
2082 };
2083 unsafe {
2084 MoveObject::new_from_execution(
2085 struct_tag.into(),
2086 has_public_transfer,
2087 old_obj_ver.unwrap_or_default(),
2088 contents,
2089 protocol_config,
2090 Mode::packages_are_predefined(),
2091 )
2092 }
2093 }
2094
2095 fn unwrap_type_tag_load(
2096 protocol_config: &ProtocolConfig,
2097 ty: Result<Type, ExecutionError>,
2098 ) -> Result<Type, ExecutionError> {
2099 if ty.is_err() && !protocol_config.type_tags_in_object_runtime() {
2100 panic!("Failed to load a type tag from the object runtime -- this shouldn't happen")
2101 } else {
2102 ty
2103 }
2104 }
2105
2106 pub enum EitherError {
2107 CommandArgument(CommandArgumentError),
2108 Execution(ExecutionError),
2109 }
2110
2111 impl From<ExecutionError> for EitherError {
2112 fn from(e: ExecutionError) -> Self {
2113 EitherError::Execution(e)
2114 }
2115 }
2116
2117 impl From<CommandArgumentError> for EitherError {
2118 fn from(e: CommandArgumentError) -> Self {
2119 EitherError::CommandArgument(e)
2120 }
2121 }
2122
2123 impl EitherError {
2124 pub fn into_execution_error(self, command_index: usize) -> ExecutionError {
2125 match self {
2126 EitherError::CommandArgument(e) => command_argument_error(e, command_index),
2127 EitherError::Execution(e) => e,
2128 }
2129 }
2130 }
2131
2132 fn convert_vm_error<S: MoveResolver<Err = SuiError>>(
2133 error: VMError,
2134 vm: &MoveVM,
2135 state_view: &S,
2136 resolve_abort_location_to_package_id: bool,
2137 ) -> ExecutionError {
2138 crate::error::convert_vm_error_impl(
2139 error,
2140 &|id: &ModuleId| {
2141 if resolve_abort_location_to_package_id {
2142 state_view.relocate(id).unwrap_or_else(|_| id.clone())
2143 } else {
2144 id.clone()
2145 }
2146 },
2147 &|id, function| {
2148 vm.load_module(id, state_view).ok().map(|module| {
2149 let fdef = module.function_def_at(function);
2150 let fhandle = module.function_handle_at(fdef.function);
2151 module.identifier_at(fhandle.name).to_string()
2152 })
2153 },
2154 )
2155 }
2156 fn convert_type_argument_error<S: MoveResolver<Err = SuiError>>(
2158 idx: usize,
2159 error: VMError,
2160 vm: &MoveVM,
2161 state_view: &S,
2162 resolve_abort_location_to_package_id: bool,
2163 ) -> ExecutionError {
2164 use sui_types::execution_status::TypeArgumentError;
2165 match error.major_status() {
2166 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
2167 ExecutionErrorKind::TypeArityMismatch.into()
2168 }
2169 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
2170 argument_idx: idx as TypeParameterIndex,
2171 kind: TypeArgumentError::TypeNotFound,
2172 }
2173 .into(),
2174 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
2175 argument_idx: idx as TypeParameterIndex,
2176 kind: TypeArgumentError::ConstraintNotSatisfied,
2177 }
2178 .into(),
2179 _ => convert_vm_error(error, vm, state_view, resolve_abort_location_to_package_id),
2180 }
2181 }
2182}