1pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8 use std::collections::BTreeSet;
9 use std::{
10 borrow::Borrow,
11 collections::{BTreeMap, HashMap},
12 sync::Arc,
13 };
14
15 use crate::adapter::new_native_extensions;
16 use crate::error::convert_vm_error;
17 use crate::execution_mode::ExecutionMode;
18 use crate::execution_value::{CommandKind, ObjectContents, TryFromValue, Value};
19 use crate::execution_value::{
20 ExecutionState, InputObjectMetadata, InputValue, ObjectValue, RawValueType, ResultValue,
21 UsageKind,
22 };
23 use crate::gas_charger::GasCharger;
24 use crate::gas_meter::SuiGasMeter;
25 use crate::programmable_transactions::linkage_view::LinkageView;
26 use crate::type_resolver::TypeTagResolver;
27 use move_binary_format::{
28 errors::{Location, PartialVMError, PartialVMResult, VMError, VMResult},
29 file_format::{CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
30 CompiledModule,
31 };
32 use move_core_types::resolver::ModuleResolver;
33 use move_core_types::vm_status::StatusCode;
34 use move_core_types::{
35 account_address::AccountAddress,
36 identifier::IdentStr,
37 language_storage::{ModuleId, StructTag, TypeTag},
38 };
39 use move_vm_runtime::native_extensions::NativeContextExtensions;
40 use move_vm_runtime::{
41 move_vm::MoveVM,
42 session::{LoadedFunctionInstantiation, SerializedReturnValues},
43 };
44 use move_vm_types::data_store::DataStore;
45 use move_vm_types::loaded_data::runtime_types::Type;
46 use sui_move_natives::object_runtime::{
47 self, get_all_uids, max_event_error, LoadedRuntimeObject, ObjectRuntime, RuntimeResults,
48 };
49 use sui_protocol_config::ProtocolConfig;
50 use sui_types::execution::ExecutionResults;
51 use sui_types::storage::PackageObject;
52 use sui_types::{
53 balance::Balance,
54 base_types::{MoveObjectType, ObjectID, SuiAddress, TxContext},
55 coin::Coin,
56 error::{ExecutionError, ExecutionErrorKind},
57 event::Event,
58 execution::ExecutionResultsV2,
59 metrics::LimitsMetrics,
60 move_package::MovePackage,
61 object::{Data, MoveObject, Object, ObjectInner, Owner},
62 storage::BackingPackageStore,
63 transaction::{Argument, CallArg, ObjectArg},
64 };
65 use sui_types::{error::command_argument_error, execution_status::CommandArgumentError};
66 use tracing::instrument;
67
68 pub struct ExecutionContext<'vm, 'state, 'a> {
70 pub protocol_config: &'a ProtocolConfig,
72 pub metrics: Arc<LimitsMetrics>,
74 pub vm: &'vm MoveVM,
76 pub linkage_view: LinkageView<'state>,
78 pub native_extensions: NativeContextExtensions<'state>,
79 pub state_view: &'state dyn ExecutionState,
81 pub tx_context: &'a mut TxContext,
84 pub gas_charger: &'a mut GasCharger,
86 additional_transfers: Vec<(SuiAddress, ObjectValue)>,
88 new_packages: Vec<MovePackage>,
90 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
92 gas: InputValue,
95 inputs: Vec<InputValue>,
97 results: Vec<Vec<ResultValue>>,
101 borrowed: HashMap<Argument, bool>,
104 }
105
106 struct AdditionalWrite {
108 recipient: Owner,
110 type_: Type,
112 has_public_transfer: bool,
114 bytes: Vec<u8>,
116 }
117
118 impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
119 #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
120 pub fn new(
121 protocol_config: &'a ProtocolConfig,
122 metrics: Arc<LimitsMetrics>,
123 vm: &'vm MoveVM,
124 state_view: &'state dyn ExecutionState,
125 tx_context: &'a mut TxContext,
126 gas_charger: &'a mut GasCharger,
127 inputs: Vec<CallArg>,
128 ) -> Result<Self, ExecutionError>
129 where
130 'a: 'state,
131 {
132 let mut linkage_view = LinkageView::new(Box::new(state_view.as_sui_resolver()));
133 let mut input_object_map = BTreeMap::new();
134 let inputs = inputs
135 .into_iter()
136 .map(|call_arg| {
137 load_call_arg(
138 protocol_config,
139 vm,
140 state_view,
141 &mut linkage_view,
142 &[],
143 &mut input_object_map,
144 call_arg,
145 )
146 })
147 .collect::<Result<_, ExecutionError>>()?;
148 let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
149 let mut gas = load_object(
150 protocol_config,
151 vm,
152 state_view,
153 &mut linkage_view,
154 &[],
155 &mut input_object_map,
156 false,
157 gas_coin,
158 )?;
159 let Some(Value::Object(ObjectValue {
163 contents: ObjectContents::Coin(coin),
164 ..
165 })) = &mut gas.inner.value
166 else {
167 invariant_violation!("Gas object should be a populated coin")
168 };
169
170 let max_gas_in_balance = gas_charger.gas_budget();
171 let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
172 invariant_violation!(
173 "Transaction input checker should check that there is enough gas"
174 );
175 };
176 coin.balance = Balance::new(new_balance);
177 gas
178 } else {
179 InputValue {
180 object_metadata: None,
181 inner: ResultValue {
182 last_usage_kind: None,
183 value: None,
184 },
185 }
186 };
187 let native_extensions = new_native_extensions(
188 state_view.as_child_resolver(),
189 input_object_map,
190 !gas_charger.is_unmetered(),
191 protocol_config,
192 metrics.clone(),
193 tx_context.epoch(),
194 );
195
196 Ok(Self {
197 protocol_config,
198 metrics,
199 vm,
200 linkage_view,
201 native_extensions,
202 state_view,
203 tx_context,
204 gas_charger,
205 gas,
206 inputs,
207 results: vec![],
208 additional_transfers: vec![],
209 new_packages: vec![],
210 user_events: vec![],
211 borrowed: HashMap::new(),
212 })
213 }
214
215 pub fn object_runtime(&mut self) -> &ObjectRuntime<'_> {
216 self.native_extensions.get()
217 }
218
219 pub fn fresh_id(&mut self) -> Result<ObjectID, ExecutionError> {
221 let object_id = self.tx_context.fresh_id();
222 let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
223 object_runtime
224 .new_id(object_id)
225 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
226 Ok(object_id)
227 }
228
229 pub fn delete_id(&mut self, object_id: ObjectID) -> Result<(), ExecutionError> {
231 let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
232 object_runtime
233 .delete_id(object_id)
234 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
235 }
236
237 pub fn set_link_context(
240 &mut self,
241 package_id: ObjectID,
242 ) -> Result<AccountAddress, ExecutionError> {
243 if self.linkage_view.has_linkage(package_id) {
244 return Ok(self
246 .linkage_view
247 .original_package_id()
248 .unwrap_or(*package_id));
249 }
250
251 let package = package_for_linkage(&self.linkage_view, package_id)
252 .map_err(|e| self.convert_vm_error(e))?;
253
254 self.linkage_view.set_linkage(package.move_package())
255 }
256
257 pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
259 load_type(
260 self.vm,
261 &mut self.linkage_view,
262 &self.new_packages,
263 type_tag,
264 )
265 }
266
267 pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
269 load_type_from_struct(
270 self.vm,
271 &mut self.linkage_view,
272 &self.new_packages,
273 struct_tag,
274 )
275 }
276
277 pub fn take_user_events(
280 &mut self,
281 module_id: &ModuleId,
282 function: FunctionDefinitionIndex,
283 last_offset: CodeOffset,
284 ) -> Result<(), ExecutionError> {
285 let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
286 let events = object_runtime.take_user_events();
287 let num_events = self.user_events.len() + events.len();
288 let max_events = self.protocol_config.max_num_event_emit();
289 if num_events as u64 > max_events {
290 let err = max_event_error(max_events)
291 .at_code_offset(function, last_offset)
292 .finish(Location::Module(module_id.clone()));
293 return Err(self.convert_vm_error(err));
294 }
295 let new_events = events
296 .into_iter()
297 .map(|(ty, tag, value)| {
298 let layout = self
299 .vm
300 .get_runtime()
301 .type_to_type_layout(&ty)
302 .map_err(|e| self.convert_vm_error(e))?;
303 let Some(bytes) = value.simple_serialize(&layout) else {
304 invariant_violation!("Failed to deserialize already serialized Move value");
305 };
306 Ok((module_id.clone(), tag, bytes))
307 })
308 .collect::<Result<Vec<_>, ExecutionError>>()?;
309 self.user_events.extend(new_events);
310 Ok(())
311 }
312
313 pub fn by_value_arg<V: TryFromValue>(
318 &mut self,
319 command_kind: CommandKind<'_>,
320 arg_idx: usize,
321 arg: Argument,
322 ) -> Result<V, ExecutionError> {
323 self.by_value_arg_(command_kind, arg)
324 .map_err(|e| command_argument_error(e, arg_idx))
325 }
326 fn by_value_arg_<V: TryFromValue>(
327 &mut self,
328 command_kind: CommandKind<'_>,
329 arg: Argument,
330 ) -> Result<V, CommandArgumentError> {
331 let shared_obj_deletion_enabled = self.protocol_config.shared_object_deletion();
332 let is_borrowed = self.arg_is_borrowed(&arg);
333 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
334 let is_copyable = if let Some(val) = val_opt {
335 val.is_copyable()
336 } else {
337 return Err(CommandArgumentError::InvalidValueUsage);
338 };
339 if !is_copyable && is_borrowed {
345 return Err(CommandArgumentError::InvalidValueUsage);
346 }
347 if matches!(arg, Argument::GasCoin)
349 && !matches!(command_kind, CommandKind::TransferObjects)
350 {
351 return Err(CommandArgumentError::InvalidGasCoinUsage);
352 }
353 if matches!(
355 input_metadata_opt,
356 Some(InputObjectMetadata::InputObject {
357 owner: Owner::Immutable,
358 ..
359 })
360 ) {
361 return Err(CommandArgumentError::InvalidObjectByValue);
362 }
363 if (
364 matches!(
366 input_metadata_opt,
367 Some(InputObjectMetadata::InputObject {
368 owner: Owner::Shared { .. },
369 ..
370 })
371 ) && !shared_obj_deletion_enabled
372 ) {
373 return Err(CommandArgumentError::InvalidObjectByValue);
374 }
375
376 if matches!(
378 input_metadata_opt,
379 Some(InputObjectMetadata::InputObject {
380 is_mutable_input: false,
381 ..
382 })
383 ) {
384 return Err(CommandArgumentError::InvalidObjectByValue);
385 }
386
387 let val = if is_copyable {
388 val_opt.as_ref().unwrap().clone()
389 } else {
390 val_opt.take().unwrap()
391 };
392 V::try_from_value(val)
393 }
394
395 pub fn borrow_arg_mut<V: TryFromValue>(
401 &mut self,
402 arg_idx: usize,
403 arg: Argument,
404 ) -> Result<V, ExecutionError> {
405 self.borrow_arg_mut_(arg)
406 .map_err(|e| command_argument_error(e, arg_idx))
407 }
408 fn borrow_arg_mut_<V: TryFromValue>(
409 &mut self,
410 arg: Argument,
411 ) -> Result<V, CommandArgumentError> {
412 if self.arg_is_borrowed(&arg) {
414 return Err(CommandArgumentError::InvalidValueUsage);
415 }
416 self.borrowed.insert(arg, true);
417 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
418 let is_copyable = if let Some(val) = val_opt {
419 val.is_copyable()
420 } else {
421 return Err(CommandArgumentError::InvalidValueUsage);
423 };
424 if let Some(InputObjectMetadata::InputObject {
425 is_mutable_input: false,
426 ..
427 }) = input_metadata_opt
428 {
429 return Err(CommandArgumentError::InvalidObjectByMutRef);
430 }
431 let val = if is_copyable {
434 val_opt.as_ref().unwrap().clone()
435 } else {
436 val_opt.take().unwrap()
437 };
438 V::try_from_value(val)
439 }
440
441 pub fn borrow_arg<V: TryFromValue>(
445 &mut self,
446 arg_idx: usize,
447 arg: Argument,
448 type_: &Type,
449 ) -> Result<V, ExecutionError> {
450 self.borrow_arg_(arg, type_)
451 .map_err(|e| command_argument_error(e, arg_idx))
452 }
453 fn borrow_arg_<V: TryFromValue>(
454 &mut self,
455 arg: Argument,
456 arg_type: &Type,
457 ) -> Result<V, CommandArgumentError> {
458 if self.arg_is_mut_borrowed(&arg) {
462 return Err(CommandArgumentError::InvalidValueUsage);
463 }
464 self.borrowed.insert(arg, false);
465 let (_input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowImm)?;
466 if val_opt.is_none() {
467 return Err(CommandArgumentError::InvalidValueUsage);
468 }
469
470 if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
472 let Type::Reference(inner) = arg_type else {
473 return Err(CommandArgumentError::InvalidValueUsage);
474 };
475 *recv_arg_type = Some(*(*inner).clone());
476 }
477
478 V::try_from_value(val_opt.as_ref().unwrap().clone())
479 }
480
481 pub fn restore_arg<Mode: ExecutionMode>(
483 &mut self,
484 updates: &mut Mode::ArgumentUpdates,
485 arg: Argument,
486 value: Value,
487 ) -> Result<(), ExecutionError> {
488 Mode::add_argument_update(self, updates, arg, &value)?;
489 let was_mut_opt = self.borrowed.remove(&arg);
490 assert_invariant!(
491 was_mut_opt.is_some() && was_mut_opt.unwrap(),
492 "Should never restore a non-mut borrowed value. \
493 The take+restore is an implementation detail of mutable references"
494 );
495 let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
497 invariant_violation!("Should be able to borrow argument to restore it")
498 };
499
500 let old_value = value_opt.replace(value);
501 assert_invariant!(
502 old_value.is_none() || old_value.unwrap().is_copyable(),
503 "Should never restore a non-taken value, unless it is copyable. \
504 The take+restore is an implementation detail of mutable references"
505 );
506
507 Ok(())
508 }
509
510 pub fn transfer_object(
512 &mut self,
513 obj: ObjectValue,
514 addr: SuiAddress,
515 ) -> Result<(), ExecutionError> {
516 self.additional_transfers.push((addr, obj));
517 Ok(())
518 }
519
520 pub fn new_package<'p>(
522 &self,
523 modules: &[CompiledModule],
524 dependencies: impl IntoIterator<Item = &'p MovePackage>,
525 ) -> Result<MovePackage, ExecutionError> {
526 MovePackage::new_initial(modules, self.protocol_config, dependencies)
527 }
528
529 pub fn upgrade_package<'p>(
531 &self,
532 storage_id: ObjectID,
533 previous_package: &MovePackage,
534 new_modules: &[CompiledModule],
535 dependencies: impl IntoIterator<Item = &'p MovePackage>,
536 ) -> Result<MovePackage, ExecutionError> {
537 previous_package.new_upgraded(
538 storage_id,
539 new_modules,
540 self.protocol_config,
541 dependencies,
542 )
543 }
544
545 pub fn write_package(&mut self, package: MovePackage) {
547 self.new_packages.push(package);
548 }
549
550 pub fn pop_package(&mut self) -> Option<MovePackage> {
555 self.new_packages.pop()
556 }
557
558 pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
560 assert_invariant!(
561 self.borrowed.values().all(|is_mut| !is_mut),
562 "all mut borrows should be restored"
563 );
564 self.borrowed = HashMap::new();
566 self.results
567 .push(results.into_iter().map(ResultValue::new).collect());
568 Ok(())
569 }
570
571 pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
573 let Self {
574 protocol_config,
575 vm,
576 linkage_view,
577 mut native_extensions,
578 tx_context,
579 gas_charger,
580 additional_transfers,
581 new_packages,
582 gas,
583 inputs,
584 results,
585 user_events,
586 ..
587 } = self;
588 let tx_digest = tx_context.digest();
589 let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
590 let mut loaded_runtime_objects = BTreeMap::new();
591 let mut additional_writes = BTreeMap::new();
592 let mut by_value_shared_objects = BTreeSet::new();
593 for input in inputs.into_iter().chain(std::iter::once(gas)) {
594 let InputValue {
595 object_metadata:
596 Some(InputObjectMetadata::InputObject {
597 is_mutable_input: true,
599 id,
600 version,
601 owner,
602 }),
603 inner: ResultValue { value, .. },
604 } = input
605 else {
606 continue;
607 };
608 loaded_runtime_objects.insert(
609 id,
610 LoadedRuntimeObject {
611 version,
612 is_modified: true,
613 },
614 );
615 if let Some(Value::Object(object_value)) = value {
616 add_additional_write(&mut additional_writes, owner, object_value)?;
617 } else if owner.is_shared() {
618 by_value_shared_objects.insert(id);
619 }
620 }
621 if !Mode::allow_arbitrary_values() {
624 for (i, command_result) in results.iter().enumerate() {
625 for (j, result_value) in command_result.iter().enumerate() {
626 let ResultValue {
627 last_usage_kind,
628 value,
629 } = result_value;
630 match value {
631 None => (),
632 Some(Value::Object(_)) => {
633 return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
634 result_idx: i as u16,
635 secondary_idx: j as u16,
636 }
637 .into())
638 }
639 Some(Value::Raw(RawValueType::Any, _)) => (),
640 Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
641 if abilities.has_drop()
647 || (abilities.has_copy()
648 && matches!(last_usage_kind, Some(UsageKind::ByValue)))
649 {
650 } else {
651 let msg = if abilities.has_copy() {
652 "The value has copy, but not drop. \
653 Its last usage must be by-value so it can be taken."
654 } else {
655 "Unused value without drop"
656 };
657 return Err(ExecutionError::new_with_source(
658 ExecutionErrorKind::UnusedValueWithoutDrop {
659 result_idx: i as u16,
660 secondary_idx: j as u16,
661 },
662 msg,
663 ));
664 }
665 }
666 Some(Value::Receiving(_, _, _)) => (),
668 }
669 }
670 }
671 }
672 for (recipient, object_value) in additional_transfers {
674 let owner = Owner::AddressOwner(recipient);
675 add_additional_write(&mut additional_writes, owner, object_value)?;
676 }
677 if let Some(gas_id) = gas_id_opt {
679 refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
680 }
681
682 let object_runtime: ObjectRuntime = native_extensions.remove();
683
684 let RuntimeResults {
685 writes,
686 user_events: remaining_events,
687 loaded_child_objects,
688 mut created_object_ids,
689 deleted_object_ids,
690 } = object_runtime.finish()?;
691 assert_invariant!(
692 remaining_events.is_empty(),
693 "Events should be taken after every Move call"
694 );
695
696 loaded_runtime_objects.extend(loaded_child_objects);
697
698 let mut written_objects = BTreeMap::new();
699 for package in new_packages {
700 let package_obj = Object::new_from_package(package, tx_digest);
701 let id = package_obj.id();
702 created_object_ids.insert(id);
703 written_objects.insert(id, package_obj);
704 }
705 for (id, additional_write) in additional_writes {
706 let AdditionalWrite {
707 recipient,
708 type_,
709 has_public_transfer,
710 bytes,
711 } = additional_write;
712 let move_object = unsafe {
714 create_written_object(
715 vm,
716 &linkage_view,
717 protocol_config,
718 &loaded_runtime_objects,
719 id,
720 type_,
721 has_public_transfer,
722 bytes,
723 )?
724 };
725 let object = Object::new_move(move_object, recipient, tx_digest);
726 written_objects.insert(id, object);
727 if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
728 loaded.is_modified = true;
729 }
730 }
731
732 for (id, (recipient, ty, value)) in writes {
733 let abilities = vm
734 .get_runtime()
735 .get_type_abilities(&ty)
736 .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
737 let has_public_transfer = abilities.has_store();
738 let layout = vm
739 .get_runtime()
740 .type_to_type_layout(&ty)
741 .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
742 let Some(bytes) = value.simple_serialize(&layout) else {
743 invariant_violation!("Failed to deserialize already serialized Move value");
744 };
745 let move_object = unsafe {
747 create_written_object(
748 vm,
749 &linkage_view,
750 protocol_config,
751 &loaded_runtime_objects,
752 id,
753 ty,
754 has_public_transfer,
755 bytes,
756 )?
757 };
758 let object = Object::new_move(move_object, recipient, tx_digest);
759 written_objects.insert(id, object);
760 }
761
762 for id in &by_value_shared_objects {
767 if let Some(obj) = written_objects.get(id) {
770 if !obj.is_shared() {
771 return Err(ExecutionError::new(
772 ExecutionErrorKind::SharedObjectOperationNotAllowed,
773 Some(
774 format!(
775 "Shared object operation on {} not allowed: \
776 cannot be frozen, transferred, or wrapped",
777 id
778 )
779 .into(),
780 ),
781 ));
782 }
783 } else {
784 if !deleted_object_ids.contains(id) {
787 return Err(ExecutionError::new(
788 ExecutionErrorKind::SharedObjectOperationNotAllowed,
789 Some(
790 format!("Shared object operation on {} not allowed: \
791 shared objects used by value must be re-shared if not deleted", id).into(),
792 ),
793 ));
794 }
795 }
796 }
797
798 let user_events = user_events
799 .into_iter()
800 .map(|(module_id, tag, contents)| {
801 Event::new(
802 module_id.address(),
803 module_id.name(),
804 tx_context.sender(),
805 tag,
806 contents,
807 )
808 })
809 .collect();
810
811 Ok(ExecutionResults::V2(ExecutionResultsV2 {
812 written_objects,
813 modified_objects: loaded_runtime_objects
814 .into_iter()
815 .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
816 .collect(),
817 created_object_ids: created_object_ids.into_iter().collect(),
818 deleted_object_ids: deleted_object_ids.into_iter().collect(),
819 user_events,
820 accumulator_events: vec![],
822 settlement_input_sui: 0,
824 settlement_output_sui: 0,
825 }))
826 }
827
828 pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
830 crate::error::convert_vm_error(error, self.vm, &self.linkage_view)
831 }
832
833 pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
835 use move_core_types::vm_status::StatusCode;
836 use sui_types::execution_status::TypeArgumentError;
837 match error.major_status() {
838 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
839 ExecutionErrorKind::TypeArityMismatch.into()
840 }
841 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
842 argument_idx: idx as TypeParameterIndex,
843 kind: TypeArgumentError::TypeNotFound,
844 }
845 .into(),
846 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
847 argument_idx: idx as TypeParameterIndex,
848 kind: TypeArgumentError::ConstraintNotSatisfied,
849 }
850 .into(),
851 _ => self.convert_vm_error(error),
852 }
853 }
854
855 fn arg_is_borrowed(&self, arg: &Argument) -> bool {
857 self.borrowed.contains_key(arg)
858 }
859
860 fn arg_is_mut_borrowed(&self, arg: &Argument) -> bool {
862 matches!(self.borrowed.get(arg), Some(true))
863 }
864
865 fn borrow_mut(
867 &mut self,
868 arg: Argument,
869 usage: UsageKind,
870 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
871 {
872 self.borrow_mut_impl(arg, Some(usage))
873 }
874
875 fn borrow_mut_impl(
878 &mut self,
879 arg: Argument,
880 update_last_usage: Option<UsageKind>,
881 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
882 {
883 let (metadata, result_value) = match arg {
884 Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
885 Argument::Input(i) => {
886 let Some(input_value) = self.inputs.get_mut(i as usize) else {
887 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
888 };
889 (input_value.object_metadata.as_ref(), &mut input_value.inner)
890 }
891 Argument::Result(i) => {
892 let Some(command_result) = self.results.get_mut(i as usize) else {
893 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
894 };
895 if command_result.len() != 1 {
896 return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
897 }
898 (None, &mut command_result[0])
899 }
900 Argument::NestedResult(i, j) => {
901 let Some(command_result) = self.results.get_mut(i as usize) else {
902 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
903 };
904 let Some(result_value) = command_result.get_mut(j as usize) else {
905 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
906 result_idx: i,
907 secondary_idx: j,
908 });
909 };
910 (None, result_value)
911 }
912 };
913 if let Some(usage) = update_last_usage {
914 result_value.last_usage_kind = Some(usage);
915 }
916 Ok((metadata, &mut result_value.value))
917 }
918
919 pub(crate) fn execute_function_bypass_visibility(
920 &mut self,
921 module: &ModuleId,
922 function_name: &IdentStr,
923 ty_args: Vec<Type>,
924 args: Vec<impl Borrow<[u8]>>,
925 ) -> VMResult<SerializedReturnValues> {
926 let gas_status = self.gas_charger.move_gas_status_mut();
927 let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
928 self.vm.get_runtime().execute_function_bypass_visibility(
929 module,
930 function_name,
931 ty_args,
932 args,
933 &mut data_store,
934 &mut SuiGasMeter(gas_status),
935 &mut self.native_extensions,
936 )
937 }
938
939 pub(crate) fn load_function(
940 &mut self,
941 module_id: &ModuleId,
942 function_name: &IdentStr,
943 type_arguments: &[Type],
944 ) -> VMResult<LoadedFunctionInstantiation> {
945 let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
946 self.vm.get_runtime().load_function(
947 module_id,
948 function_name,
949 type_arguments,
950 &mut data_store,
951 )
952 }
953
954 pub(crate) fn make_object_value(
955 &mut self,
956 type_: MoveObjectType,
957 has_public_transfer: bool,
958 used_in_non_entry_move_call: bool,
959 contents: &[u8],
960 ) -> Result<ObjectValue, ExecutionError> {
961 make_object_value(
962 self.protocol_config,
963 self.vm,
964 &mut self.linkage_view,
965 &self.new_packages,
966 type_,
967 has_public_transfer,
968 used_in_non_entry_move_call,
969 contents,
970 )
971 }
972
973 pub fn publish_module_bundle(
974 &mut self,
975 modules: Vec<Vec<u8>>,
976 sender: AccountAddress,
977 ) -> VMResult<()> {
978 let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
981 self.vm.get_runtime().publish_module_bundle(
982 modules,
983 sender,
984 &mut data_store,
985 &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
986 )
987 }
988 }
989
990 impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
991 fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
992 self.vm
993 .get_runtime()
994 .get_type_tag(type_)
995 .map_err(|e| self.convert_vm_error(e))
996 }
997 }
998
999 fn package_for_linkage(
1002 linkage_view: &LinkageView,
1003 package_id: ObjectID,
1004 ) -> VMResult<PackageObject> {
1005 use move_binary_format::errors::PartialVMError;
1006 use move_core_types::vm_status::StatusCode;
1007
1008 match linkage_view.get_package_object(&package_id) {
1009 Ok(Some(package)) => Ok(package),
1010 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1011 .with_message(format!("Cannot find link context {package_id} in store"))
1012 .finish(Location::Undefined)),
1013 Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1014 .with_message(format!(
1015 "Error loading link context {package_id} from store: {err}"
1016 ))
1017 .finish(Location::Undefined)),
1018 }
1019 }
1020
1021 pub fn load_type_from_struct(
1022 vm: &MoveVM,
1023 linkage_view: &mut LinkageView,
1024 new_packages: &[MovePackage],
1025 struct_tag: &StructTag,
1026 ) -> VMResult<Type> {
1027 fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1028 Err(PartialVMError::new(code).finish(Location::Undefined))
1029 }
1030
1031 let StructTag {
1032 address,
1033 module,
1034 name,
1035 type_params,
1036 } = struct_tag;
1037
1038 let defining_id = ObjectID::from_address(*address);
1040 let package = package_for_linkage(linkage_view, defining_id)?;
1041
1042 let original_address = linkage_view
1045 .set_linkage(package.move_package())
1046 .map_err(|e| {
1047 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1048 .with_message(e.to_string())
1049 .finish(Location::Undefined)
1050 })?;
1051
1052 let runtime_id = ModuleId::new(original_address, module.clone());
1053 let data_store = SuiDataStore::new(linkage_view, new_packages);
1054 let res = vm.get_runtime().load_struct(&runtime_id, name, &data_store);
1055 linkage_view.reset_linkage();
1056 let (idx, struct_type) = res?;
1057
1058 let type_param_constraints = struct_type.type_param_constraints();
1060 if type_param_constraints.len() != type_params.len() {
1061 return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1062 }
1063
1064 if type_params.is_empty() {
1065 Ok(Type::Datatype(idx))
1066 } else {
1067 let loaded_type_params = type_params
1068 .iter()
1069 .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1070 .collect::<VMResult<Vec<_>>>()?;
1071
1072 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1074 let abilities = vm.get_runtime().get_type_abilities(param)?;
1075 if !constraint.is_subset(abilities) {
1076 return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1077 }
1078 }
1079
1080 Ok(Type::DatatypeInstantiation(Box::new((
1081 idx,
1082 loaded_type_params,
1083 ))))
1084 }
1085 }
1086
1087 pub fn load_type(
1090 vm: &MoveVM,
1091 linkage_view: &mut LinkageView,
1092 new_packages: &[MovePackage],
1093 type_tag: &TypeTag,
1094 ) -> VMResult<Type> {
1095 Ok(match type_tag {
1096 TypeTag::Bool => Type::Bool,
1097 TypeTag::U8 => Type::U8,
1098 TypeTag::U16 => Type::U16,
1099 TypeTag::U32 => Type::U32,
1100 TypeTag::U64 => Type::U64,
1101 TypeTag::U128 => Type::U128,
1102 TypeTag::U256 => Type::U256,
1103 TypeTag::Address => Type::Address,
1104 TypeTag::Signer => Type::Signer,
1105
1106 TypeTag::Vector(inner) => {
1107 Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1108 }
1109 TypeTag::Struct(struct_tag) => {
1110 return load_type_from_struct(vm, linkage_view, new_packages, struct_tag)
1111 }
1112 })
1113 }
1114
1115 pub(crate) fn make_object_value(
1116 protocol_config: &ProtocolConfig,
1117 vm: &MoveVM,
1118 linkage_view: &mut LinkageView,
1119 new_packages: &[MovePackage],
1120 type_: MoveObjectType,
1121 has_public_transfer: bool,
1122 used_in_non_entry_move_call: bool,
1123 contents: &[u8],
1124 ) -> Result<ObjectValue, ExecutionError> {
1125 let contents = if type_.is_coin() {
1126 let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1127 invariant_violation!("Could not deserialize a coin")
1128 };
1129 ObjectContents::Coin(coin)
1130 } else {
1131 ObjectContents::Raw(contents.to_vec())
1132 };
1133
1134 let tag: StructTag = type_.into();
1135 let type_ = load_type_from_struct(vm, linkage_view, new_packages, &tag)
1136 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1137 let has_public_transfer = if protocol_config.recompute_has_public_transfer_in_execution() {
1138 let abilities = vm
1139 .get_runtime()
1140 .get_type_abilities(&type_)
1141 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1142 abilities.has_store()
1143 } else {
1144 has_public_transfer
1145 };
1146 Ok(ObjectValue {
1147 type_,
1148 has_public_transfer,
1149 used_in_non_entry_move_call,
1150 contents,
1151 })
1152 }
1153
1154 pub(crate) fn value_from_object(
1155 protocol_config: &ProtocolConfig,
1156 vm: &MoveVM,
1157 linkage_view: &mut LinkageView,
1158 new_packages: &[MovePackage],
1159 object: &Object,
1160 ) -> Result<ObjectValue, ExecutionError> {
1161 let ObjectInner {
1162 data: Data::Move(object),
1163 ..
1164 } = object.as_inner()
1165 else {
1166 invariant_violation!("Expected a Move object");
1167 };
1168
1169 let used_in_non_entry_move_call = false;
1170 make_object_value(
1171 protocol_config,
1172 vm,
1173 linkage_view,
1174 new_packages,
1175 object.type_().clone(),
1176 object.has_public_transfer(),
1177 used_in_non_entry_move_call,
1178 object.contents(),
1179 )
1180 }
1181
1182 fn load_object(
1184 protocol_config: &ProtocolConfig,
1185 vm: &MoveVM,
1186 state_view: &dyn ExecutionState,
1187 linkage_view: &mut LinkageView,
1188 new_packages: &[MovePackage],
1189 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1190 override_as_immutable: bool,
1191 id: ObjectID,
1192 ) -> Result<InputValue, ExecutionError> {
1193 let Some(obj) = state_view.read_object(&id) else {
1194 invariant_violation!("Object {} does not exist yet", id);
1196 };
1197 assert_invariant!(
1199 !override_as_immutable || matches!(obj.owner, Owner::Shared { .. }),
1200 "override_as_immutable should only be set for shared objects"
1201 );
1202 let is_mutable_input = match obj.owner {
1203 Owner::AddressOwner(_) => true,
1204 Owner::Shared { .. } => !override_as_immutable,
1205 Owner::Immutable => false,
1206 Owner::ObjectOwner(_) => {
1207 invariant_violation!("ObjectOwner objects cannot be input")
1209 }
1210 Owner::ConsensusAddressOwner { .. } => {
1211 unimplemented!("ConsensusAddressOwner does not exist for this execution version")
1212 }
1213 };
1214 let owner = obj.owner.clone();
1215 let version = obj.version();
1216 let object_metadata = InputObjectMetadata::InputObject {
1217 id,
1218 is_mutable_input,
1219 owner: owner.clone(),
1220 version,
1221 };
1222 let obj_value = value_from_object(protocol_config, vm, linkage_view, new_packages, obj)?;
1223 let contained_uids = {
1224 let fully_annotated_layout = vm
1225 .get_runtime()
1226 .type_to_fully_annotated_layout(&obj_value.type_)
1227 .map_err(|e| convert_vm_error(e, vm, linkage_view))?;
1228 let mut bytes = vec![];
1229 obj_value.write_bcs_bytes(&mut bytes);
1230 match get_all_uids(&fully_annotated_layout, &bytes) {
1231 Err(e) => {
1232 invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1233 }
1234 Ok(uids) => uids,
1235 }
1236 };
1237 let runtime_input = object_runtime::InputObject {
1238 contained_uids,
1239 owner,
1240 version,
1241 };
1242 let prev = input_object_map.insert(id, runtime_input);
1243 assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1245 Ok(InputValue::new_object(object_metadata, obj_value))
1246 }
1247
1248 fn load_call_arg(
1250 protocol_config: &ProtocolConfig,
1251 vm: &MoveVM,
1252 state_view: &dyn ExecutionState,
1253 linkage_view: &mut LinkageView,
1254 new_packages: &[MovePackage],
1255 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1256 call_arg: CallArg,
1257 ) -> Result<InputValue, ExecutionError> {
1258 Ok(match call_arg {
1259 CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1260 CallArg::Object(obj_arg) => load_object_arg(
1261 protocol_config,
1262 vm,
1263 state_view,
1264 linkage_view,
1265 new_packages,
1266 input_object_map,
1267 obj_arg,
1268 )?,
1269 CallArg::FundsWithdrawal(_) => {
1270 unreachable!("Impossible to hit BalanceWithdraw in v2")
1271 }
1272 })
1273 }
1274
1275 fn load_object_arg(
1277 protocol_config: &ProtocolConfig,
1278 vm: &MoveVM,
1279 state_view: &dyn ExecutionState,
1280 linkage_view: &mut LinkageView,
1281 new_packages: &[MovePackage],
1282 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1283 obj_arg: ObjectArg,
1284 ) -> Result<InputValue, ExecutionError> {
1285 match obj_arg {
1286 ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1287 protocol_config,
1288 vm,
1289 state_view,
1290 linkage_view,
1291 new_packages,
1292 input_object_map,
1293 false,
1294 id,
1295 ),
1296 ObjectArg::SharedObject { id, mutability, .. } => load_object(
1297 protocol_config,
1298 vm,
1299 state_view,
1300 linkage_view,
1301 new_packages,
1302 input_object_map,
1303 !mutability.is_exclusive(),
1304 id,
1305 ),
1306 ObjectArg::Receiving((id, version, _)) => {
1307 Ok(InputValue::new_receiving_object(id, version))
1308 }
1309 }
1310 }
1311
1312 fn add_additional_write(
1314 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1315 owner: Owner,
1316 object_value: ObjectValue,
1317 ) -> Result<(), ExecutionError> {
1318 let ObjectValue {
1319 type_,
1320 has_public_transfer,
1321 contents,
1322 ..
1323 } = object_value;
1324 let bytes = match contents {
1325 ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1326 ObjectContents::Raw(bytes) => bytes,
1327 };
1328 let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
1329 ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
1330 })?;
1331 let additional_write = AdditionalWrite {
1332 recipient: owner,
1333 type_,
1334 has_public_transfer,
1335 bytes,
1336 };
1337 additional_writes.insert(object_id, additional_write);
1338 Ok(())
1339 }
1340
1341 fn refund_max_gas_budget(
1344 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1345 gas_charger: &mut GasCharger,
1346 gas_id: ObjectID,
1347 ) -> Result<(), ExecutionError> {
1348 let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1349 invariant_violation!("Gas object cannot be wrapped or destroyed")
1350 };
1351 let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1352 invariant_violation!("Gas object must be a coin")
1353 };
1354 let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1355 return Err(ExecutionError::new_with_source(
1356 ExecutionErrorKind::CoinBalanceOverflow,
1357 "Gas coin too large after returning the max gas budget",
1358 ));
1359 };
1360 coin.balance = Balance::new(new_balance);
1361 *bytes = coin.to_bcs_bytes();
1362 Ok(())
1363 }
1364
1365 unsafe fn create_written_object(
1371 vm: &MoveVM,
1372 linkage_view: &LinkageView,
1373 protocol_config: &ProtocolConfig,
1374 objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1375 id: ObjectID,
1376 type_: Type,
1377 has_public_transfer: bool,
1378 contents: Vec<u8>,
1379 ) -> Result<MoveObject, ExecutionError> {
1380 debug_assert_eq!(
1381 id,
1382 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1383 );
1384 let old_obj_ver = objects_modified_at
1385 .get(&id)
1386 .map(|obj: &LoadedRuntimeObject| obj.version);
1387
1388 let type_tag = vm
1389 .get_runtime()
1390 .get_type_tag(&type_)
1391 .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1392
1393 let struct_tag = match type_tag {
1394 TypeTag::Struct(inner) => *inner,
1395 _ => invariant_violation!("Non struct type for object"),
1396 };
1397 MoveObject::new_from_execution(
1398 struct_tag.into(),
1399 has_public_transfer,
1400 old_obj_ver.unwrap_or_default(),
1401 contents,
1402 protocol_config,
1403 false,
1404 )
1405 }
1406
1407 pub(crate) struct SuiDataStore<'state, 'a> {
1414 linkage_view: &'a LinkageView<'state>,
1415 new_packages: &'a [MovePackage],
1416 }
1417
1418 impl<'state, 'a> SuiDataStore<'state, 'a> {
1419 pub(crate) fn new(
1420 linkage_view: &'a LinkageView<'state>,
1421 new_packages: &'a [MovePackage],
1422 ) -> Self {
1423 Self {
1424 linkage_view,
1425 new_packages,
1426 }
1427 }
1428
1429 fn get_module(&self, module_id: &ModuleId) -> Option<&Vec<u8>> {
1430 for package in self.new_packages {
1431 let module = package.get_module(module_id);
1432 if module.is_some() {
1433 return module;
1434 }
1435 }
1436 None
1437 }
1438 }
1439
1440 impl DataStore for SuiDataStore<'_, '_> {
1443 fn link_context(&self) -> AccountAddress {
1444 self.linkage_view.link_context()
1445 }
1446
1447 fn relocate(&self, module_id: &ModuleId) -> PartialVMResult<ModuleId> {
1448 self.linkage_view.relocate(module_id).map_err(|err| {
1449 PartialVMError::new(StatusCode::LINKER_ERROR)
1450 .with_message(format!("Error relocating {module_id}: {err:?}"))
1451 })
1452 }
1453
1454 fn defining_module(
1455 &self,
1456 runtime_id: &ModuleId,
1457 struct_: &IdentStr,
1458 ) -> PartialVMResult<ModuleId> {
1459 self.linkage_view
1460 .defining_module(runtime_id, struct_)
1461 .map_err(|err| {
1462 PartialVMError::new(StatusCode::LINKER_ERROR).with_message(format!(
1463 "Error finding defining module for {runtime_id}::{struct_}: {err:?}"
1464 ))
1465 })
1466 }
1467
1468 fn load_module(&self, module_id: &ModuleId) -> VMResult<Vec<u8>> {
1469 if let Some(bytes) = self.get_module(module_id) {
1470 return Ok(bytes.clone());
1471 }
1472 match self.linkage_view.get_module(module_id) {
1473 Ok(Some(bytes)) => Ok(bytes),
1474 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1475 .with_message(format!("Cannot find {:?} in data cache", module_id))
1476 .finish(Location::Undefined)),
1477 Err(err) => {
1478 let msg = format!("Unexpected storage error: {:?}", err);
1479 Err(
1480 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1481 .with_message(msg)
1482 .finish(Location::Undefined),
1483 )
1484 }
1485 }
1486 }
1487
1488 fn publish_module(&mut self, _module_id: &ModuleId, _blob: Vec<u8>) -> VMResult<()> {
1489 Ok(())
1492 }
1493 }
1494}