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