1pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8
9 use sui_types::storage::StorageView;
10
11 use std::{
12 collections::{BTreeMap, BTreeSet, HashMap},
13 sync::Arc,
14 };
15
16 use crate::adapter::new_native_extensions;
17 use crate::error::convert_vm_error;
18 use crate::execution_mode::ExecutionMode;
19 use crate::execution_value::{
20 CommandKind, InputObjectMetadata, InputValue, ObjectContents, ObjectValue, RawValueType,
21 ResultValue, TryFromValue, UsageKind, Value,
22 };
23 use crate::gas_charger::GasCharger;
24 use crate::programmable_transactions::linkage_view::{LinkageInfo, LinkageView, SavedLinkage};
25 use crate::type_resolver::TypeTagResolver;
26 use move_binary_format::{
27 errors::{Location, VMError, VMResult},
28 file_format::{CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
29 CompiledModule,
30 };
31 use move_core_types::{
32 account_address::AccountAddress,
33 language_storage::{ModuleId, StructTag, TypeTag},
34 };
35 use move_vm_runtime::{move_vm::MoveVM, session::Session};
36 use move_vm_types::loaded_data::runtime_types::Type;
37 use sui_move_natives::object_runtime::{
38 self, get_all_uids, max_event_error, ObjectRuntime, RuntimeResults,
39 };
40 use sui_protocol_config::ProtocolConfig;
41 use sui_types::execution_status::CommandArgumentError;
42 use sui_types::storage::PackageObject;
43 use sui_types::{
44 balance::Balance,
45 base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress, TxContext},
46 coin::Coin,
47 error::{command_argument_error, ExecutionError},
48 event::Event,
49 execution::{ExecutionResults, ExecutionResultsV1},
50 execution_status::ExecutionErrorKind,
51 metrics::ExecutionMetrics,
52 move_package::MovePackage,
53 object::{Data, MoveObject, Object, ObjectInner, Owner},
54 storage::{
55 BackingPackageStore, DeleteKind, DeleteKindWithOldVersion, ObjectChange,
56 RuntimeObjectResolver, WriteKind,
57 },
58 transaction::{Argument, CallArg, ObjectArg},
59 };
60
61 pub struct ExecutionContext<'vm, 'state, 'a> {
63 pub protocol_config: &'a ProtocolConfig,
65 pub metrics: Arc<ExecutionMetrics>,
67 pub vm: &'vm MoveVM,
69 pub state_view: &'state dyn StorageView,
71 pub tx_context: &'a mut TxContext,
74 pub gas_charger: &'a mut GasCharger,
76 pub session: Session<'state, 'vm, LinkageView<'state>>,
78 additional_transfers: Vec<(SuiAddress, ObjectValue)>,
80 new_packages: Vec<Object>,
82 user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
84 gas: InputValue,
87 inputs: Vec<InputValue>,
89 results: Vec<Vec<ResultValue>>,
93 borrowed: HashMap<Argument, bool>,
96 }
97
98 struct AdditionalWrite {
100 recipient: Owner,
102 type_: Type,
104 has_public_transfer: bool,
106 bytes: Vec<u8>,
108 }
109
110 impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
111 pub fn new(
112 protocol_config: &'a ProtocolConfig,
113 metrics: Arc<ExecutionMetrics>,
114 vm: &'vm MoveVM,
115 state_view: &'state dyn StorageView,
116 tx_context: &'a mut TxContext,
117 gas_charger: &'a mut GasCharger,
118 inputs: Vec<CallArg>,
119 ) -> Result<Self, ExecutionError> {
120 let init_linkage = if protocol_config.package_upgrades() {
121 LinkageInfo::Unset
122 } else {
123 LinkageInfo::Universal
124 };
125
126 let linkage = LinkageView::new(Box::new(state_view), init_linkage);
129 let mut tmp_session = new_session(
130 vm,
131 linkage,
132 state_view,
133 BTreeMap::new(),
134 !gas_charger.is_unmetered(),
135 protocol_config,
136 metrics.clone(),
137 );
138 let mut input_object_map = BTreeMap::new();
139 let inputs = inputs
140 .into_iter()
141 .map(|call_arg| {
142 load_call_arg(
143 vm,
144 state_view,
145 &mut tmp_session,
146 &mut input_object_map,
147 call_arg,
148 )
149 })
150 .collect::<Result<_, ExecutionError>>()?;
151 let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
152 let mut gas = load_object(
153 vm,
154 state_view,
155 &mut tmp_session,
156 &mut input_object_map,
157 false,
158 gas_coin,
159 )?;
160 let Some(Value::Object(ObjectValue {
164 contents: ObjectContents::Coin(coin),
165 ..
166 })) = &mut gas.inner.value
167 else {
168 invariant_violation!("Gas object should be a populated coin")
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 (res, linkage) = tmp_session.finish();
190 let change_set = res.map_err(|e| crate::error::convert_vm_error(e, vm, &linkage))?;
191 assert_invariant!(change_set.accounts().is_empty(), "Change set must be empty");
192 let session = new_session(
194 vm,
195 linkage,
196 state_view,
197 input_object_map,
198 !gas_charger.is_unmetered(),
199 protocol_config,
200 metrics.clone(),
201 );
202
203 Ok(Self {
204 protocol_config,
205 metrics,
206 vm,
207 state_view,
208 tx_context,
209 gas_charger,
210 session,
211 gas,
212 inputs,
213 results: vec![],
214 additional_transfers: vec![],
215 new_packages: vec![],
216 user_events: vec![],
217 borrowed: HashMap::new(),
218 })
219 }
220
221 pub fn fresh_id(&mut self) -> Result<ObjectID, ExecutionError> {
223 let object_id = self.tx_context.fresh_id();
224 let object_runtime: &mut ObjectRuntime = self.session.get_native_extensions().get_mut();
225 object_runtime
226 .new_id(object_id)
227 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
228 Ok(object_id)
229 }
230
231 pub fn delete_id(&mut self, object_id: ObjectID) -> Result<(), ExecutionError> {
233 let object_runtime: &mut ObjectRuntime = self.session.get_native_extensions().get_mut();
234 object_runtime
235 .delete_id(object_id)
236 .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
237 }
238
239 pub fn set_link_context(
242 &mut self,
243 package_id: ObjectID,
244 ) -> Result<AccountAddress, ExecutionError> {
245 let resolver = self.session.get_resolver();
246 if resolver.has_linkage(package_id) {
247 return Ok(resolver.original_package_id().unwrap_or(*package_id));
249 }
250
251 let package = package_for_linkage(&self.session, package_id)
252 .map_err(|e| self.convert_vm_error(e))?;
253
254 set_linkage(&mut self.session, package.move_package())
255 }
256
257 pub fn set_linkage(
260 &mut self,
261 package: &MovePackage,
262 ) -> Result<AccountAddress, ExecutionError> {
263 set_linkage(&mut self.session, package)
264 }
265
266 pub fn reset_linkage(&mut self) {
269 reset_linkage(&mut self.session);
270 }
271
272 pub fn steal_linkage(&mut self) -> Option<SavedLinkage> {
274 steal_linkage(&mut self.session)
275 }
276
277 pub fn restore_linkage(
279 &mut self,
280 saved: Option<SavedLinkage>,
281 ) -> Result<(), ExecutionError> {
282 restore_linkage(&mut self.session, saved)
283 }
284
285 pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
287 load_type(&mut self.session, type_tag)
288 }
289
290 pub fn take_user_events(
293 &mut self,
294 module_id: &ModuleId,
295 function: FunctionDefinitionIndex,
296 last_offset: CodeOffset,
297 ) -> Result<(), ExecutionError> {
298 let object_runtime: &mut ObjectRuntime = self.session.get_native_extensions().get_mut();
299 let events = object_runtime.take_user_events();
300 let num_events = self.user_events.len() + events.len();
301 let max_events = self.protocol_config.max_num_event_emit();
302 if num_events as u64 > max_events {
303 let err = max_event_error(max_events)
304 .at_code_offset(function, last_offset)
305 .finish(Location::Module(module_id.clone()));
306 return Err(self.convert_vm_error(err));
307 }
308 let new_events = events
309 .into_iter()
310 .map(|(ty, tag, value)| {
311 let layout = self
312 .session
313 .type_to_type_layout(&ty)
314 .map_err(|e| self.convert_vm_error(e))?;
315 let Some(bytes) = value.simple_serialize(&layout) else {
316 invariant_violation!("Failed to deserialize already serialized Move value");
317 };
318 Ok((module_id.clone(), tag, bytes))
319 })
320 .collect::<Result<Vec<_>, ExecutionError>>()?;
321 self.user_events.extend(new_events);
322 Ok(())
323 }
324
325 pub fn by_value_arg<V: TryFromValue>(
330 &mut self,
331 command_kind: CommandKind<'_>,
332 arg_idx: usize,
333 arg: Argument,
334 ) -> Result<V, ExecutionError> {
335 self.by_value_arg_(command_kind, arg)
336 .map_err(|e| command_argument_error(e, arg_idx))
337 }
338 fn by_value_arg_<V: TryFromValue>(
339 &mut self,
340 command_kind: CommandKind<'_>,
341 arg: Argument,
342 ) -> Result<V, CommandArgumentError> {
343 let is_borrowed = self.arg_is_borrowed(&arg);
344 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
345 let is_copyable = if let Some(val) = val_opt {
346 val.is_copyable()
347 } else {
348 return Err(CommandArgumentError::InvalidValueUsage);
349 };
350 if !is_copyable && is_borrowed {
356 return Err(CommandArgumentError::InvalidValueUsage);
357 }
358 if matches!(arg, Argument::GasCoin)
360 && !matches!(command_kind, CommandKind::TransferObjects)
361 {
362 return Err(CommandArgumentError::InvalidGasCoinUsage);
363 }
364 if matches!(
366 input_metadata_opt,
367 Some(InputObjectMetadata::InputObject {
368 owner: Owner::Immutable | Owner::Shared { .. },
369 ..
370 })
371 ) {
372 return Err(CommandArgumentError::InvalidObjectByValue);
373 }
374 let val = if is_copyable {
375 val_opt.as_ref().unwrap().clone()
376 } else {
377 val_opt.take().unwrap()
378 };
379 V::try_from_value(val)
380 }
381
382 pub fn borrow_arg_mut<V: TryFromValue>(
388 &mut self,
389 arg_idx: usize,
390 arg: Argument,
391 ) -> Result<V, ExecutionError> {
392 self.borrow_arg_mut_(arg)
393 .map_err(|e| command_argument_error(e, arg_idx))
394 }
395 fn borrow_arg_mut_<V: TryFromValue>(
396 &mut self,
397 arg: Argument,
398 ) -> Result<V, CommandArgumentError> {
399 if self.arg_is_borrowed(&arg) {
401 return Err(CommandArgumentError::InvalidValueUsage);
402 }
403 self.borrowed.insert(arg, true);
404 let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
405 let is_copyable = if let Some(val) = val_opt {
406 val.is_copyable()
407 } else {
408 return Err(CommandArgumentError::InvalidValueUsage);
410 };
411 if let Some(InputObjectMetadata::InputObject {
412 is_mutable_input: false,
413 ..
414 }) = input_metadata_opt
415 {
416 return Err(CommandArgumentError::InvalidObjectByMutRef);
417 }
418 let val = if is_copyable {
421 val_opt.as_ref().unwrap().clone()
422 } else {
423 val_opt.take().unwrap()
424 };
425 V::try_from_value(val)
426 }
427
428 pub fn borrow_arg<V: TryFromValue>(
432 &mut self,
433 arg_idx: usize,
434 arg: Argument,
435 ) -> Result<V, ExecutionError> {
436 self.borrow_arg_(arg)
437 .map_err(|e| command_argument_error(e, arg_idx))
438 }
439 fn borrow_arg_<V: TryFromValue>(
440 &mut self,
441 arg: Argument,
442 ) -> Result<V, CommandArgumentError> {
443 if self.arg_is_mut_borrowed(&arg) {
447 return Err(CommandArgumentError::InvalidValueUsage);
448 }
449 self.borrowed.insert(arg, false);
450 let (_input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowImm)?;
451 if val_opt.is_none() {
452 return Err(CommandArgumentError::InvalidValueUsage);
453 }
454 V::try_from_value(val_opt.as_ref().unwrap().clone())
455 }
456
457 pub fn restore_arg<Mode: ExecutionMode>(
459 &mut self,
460 updates: &mut Mode::ArgumentUpdates,
461 arg: Argument,
462 value: Value,
463 ) -> Result<(), ExecutionError> {
464 Mode::add_argument_update(self, updates, arg, &value)?;
465 let was_mut_opt = self.borrowed.remove(&arg);
466 assert_invariant!(
467 was_mut_opt.is_some() && was_mut_opt.unwrap(),
468 "Should never restore a non-mut borrowed value. \
469 The take+restore is an implementation detail of mutable references"
470 );
471 let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
473 invariant_violation!("Should be able to borrow argument to restore it")
474 };
475 let old_value = value_opt.replace(value);
476 assert_invariant!(
477 old_value.is_none() || old_value.unwrap().is_copyable(),
478 "Should never restore a non-taken value, unless it is copyable. \
479 The take+restore is an implementation detail of mutable references"
480 );
481 Ok(())
482 }
483
484 pub fn transfer_object(
486 &mut self,
487 obj: ObjectValue,
488 addr: SuiAddress,
489 ) -> Result<(), ExecutionError> {
490 self.additional_transfers.push((addr, obj));
491 Ok(())
492 }
493
494 pub fn new_package<'p>(
496 &self,
497 modules: &[CompiledModule],
498 dependencies: impl IntoIterator<Item = &'p MovePackage>,
499 ) -> Result<Object, ExecutionError> {
500 Object::new_package(
501 modules,
502 self.tx_context.digest(),
503 self.protocol_config,
504 dependencies,
505 )
506 }
507
508 pub fn upgrade_package<'p>(
510 &self,
511 storage_id: ObjectID,
512 previous_package: &MovePackage,
513 new_modules: &[CompiledModule],
514 dependencies: impl IntoIterator<Item = &'p MovePackage>,
515 ) -> Result<Object, ExecutionError> {
516 Object::new_upgraded_package(
517 previous_package,
518 storage_id,
519 new_modules,
520 self.tx_context.digest(),
521 self.protocol_config,
522 dependencies,
523 )
524 }
525
526 pub fn write_package(&mut self, package: Object) -> Result<(), ExecutionError> {
528 assert_invariant!(package.is_package(), "Must be a package");
529 self.new_packages.push(package);
530 Ok(())
531 }
532
533 pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
535 assert_invariant!(
536 self.borrowed.values().all(|is_mut| !is_mut),
537 "all mut borrows should be restored"
538 );
539 self.borrowed = HashMap::new();
541 self.results
542 .push(results.into_iter().map(ResultValue::new).collect());
543 Ok(())
544 }
545
546 pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
548 let Self {
549 protocol_config,
550 metrics,
551 vm,
552 state_view,
553 tx_context,
554 gas_charger,
555 session,
556 additional_transfers,
557 new_packages,
558 gas,
559 inputs,
560 results,
561 user_events,
562 ..
563 } = self;
564 let tx_digest = tx_context.digest();
565 let mut additional_writes = BTreeMap::new();
566 let mut input_object_metadata = BTreeMap::new();
567 let mut by_value_inputs = BTreeSet::new();
571 let mut add_input_object_write = |input| -> Result<(), ExecutionError> {
572 let InputValue {
573 object_metadata: object_metadata_opt,
574 inner: ResultValue { value, .. },
575 } = input;
576 let Some(object_metadata) = object_metadata_opt else {
577 return Ok(());
578 };
579 let InputObjectMetadata::InputObject {
580 is_mutable_input,
581 owner,
582 id,
583 ..
584 } = &object_metadata
585 else {
586 unreachable!("Found non-input object metadata for input object when adding writes to input objects -- impossible in v0");
587 };
588 input_object_metadata.insert(object_metadata.id(), object_metadata.clone());
589 let Some(Value::Object(object_value)) = value else {
590 by_value_inputs.insert(*id);
591 return Ok(());
592 };
593 if *is_mutable_input {
594 add_additional_write(&mut additional_writes, owner.clone(), object_value)?;
595 }
596 Ok(())
597 };
598 let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
599 add_input_object_write(gas)?;
600 for input in inputs {
601 add_input_object_write(input)?
602 }
603 if !Mode::allow_arbitrary_values() {
606 for (i, command_result) in results.iter().enumerate() {
607 for (j, result_value) in command_result.iter().enumerate() {
608 let ResultValue {
609 last_usage_kind,
610 value,
611 } = result_value;
612 match value {
613 None => (),
614 Some(Value::Object(_)) => {
615 return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
616 result_idx: i as u16,
617 secondary_idx: j as u16,
618 }
619 .into())
620 }
621 Some(Value::Raw(RawValueType::Any, _)) => (),
622 Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
623 if abilities.has_drop()
629 || (abilities.has_copy()
630 && matches!(last_usage_kind, Some(UsageKind::ByValue)))
631 {
632 } else {
633 let msg = if abilities.has_copy() {
634 "The value has copy, but not drop. \
635 Its last usage must be by-value so it can be taken."
636 } else {
637 "Unused value without drop"
638 };
639 return Err(ExecutionError::new_with_source(
640 ExecutionErrorKind::UnusedValueWithoutDrop {
641 result_idx: i as u16,
642 secondary_idx: j as u16,
643 },
644 msg,
645 ));
646 }
647 }
648 Some(Value::Receiving(_, _, _)) => {
649 unreachable!("Impossible to hit Receiving in v0")
650 }
651 }
652 }
653 }
654 }
655 for (recipient, object_value) in additional_transfers {
657 let owner = Owner::AddressOwner(recipient);
658 add_additional_write(&mut additional_writes, owner, object_value)?;
659 }
660 if let Some(gas_id) = gas_id_opt {
662 refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
663 }
664
665 let (res, linkage) = session.finish_with_extensions();
666 let (_, mut native_context_extensions) =
667 res.map_err(|e| convert_vm_error(e, vm, &linkage))?;
668 let object_runtime: ObjectRuntime = native_context_extensions.remove();
669 let new_ids = object_runtime.new_ids().clone();
670 let external_transfers = additional_writes.keys().copied().collect();
672 let RuntimeResults {
673 writes,
674 deletions,
675 user_events: remaining_events,
676 loaded_child_objects,
677 } = object_runtime.finish(by_value_inputs, external_transfers)?;
678 assert_invariant!(
679 remaining_events.is_empty(),
680 "Events should be taken after every Move call"
681 );
682 let mut object_changes = BTreeMap::new();
683 for package in new_packages {
684 let id = package.id();
685 let change = ObjectChange::Write(package, WriteKind::Create);
686 object_changes.insert(id, change);
687 }
688 let tmp_session = new_session(
691 vm,
692 linkage,
693 state_view,
694 BTreeMap::new(),
695 !gas_charger.is_unmetered(),
696 protocol_config,
697 metrics,
698 );
699 for (id, additional_write) in additional_writes {
700 let AdditionalWrite {
701 recipient,
702 type_,
703 has_public_transfer,
704 bytes,
705 } = additional_write;
706 let write_kind = if input_object_metadata.contains_key(&id)
707 || loaded_child_objects.contains_key(&id)
708 {
709 assert_invariant!(
710 !new_ids.contains_key(&id),
711 "new id should not be in mutations"
712 );
713 WriteKind::Mutate
714 } else if new_ids.contains_key(&id) {
715 WriteKind::Create
716 } else {
717 WriteKind::Unwrap
718 };
719 let move_object = unsafe {
721 create_written_object(
722 vm,
723 &tmp_session,
724 protocol_config,
725 &input_object_metadata,
726 &loaded_child_objects,
727 id,
728 type_,
729 has_public_transfer,
730 bytes,
731 write_kind,
732 )?
733 };
734 let object = Object::new_move(move_object, recipient, tx_digest);
735 let change = ObjectChange::Write(object, write_kind);
736 object_changes.insert(id, change);
737 }
738
739 for (id, (write_kind, recipient, ty, value)) in writes {
740 let abilities = tmp_session
741 .get_type_abilities(&ty)
742 .map_err(|e| convert_vm_error(e, vm, tmp_session.get_resolver()))?;
743 let has_public_transfer = abilities.has_store();
744 let layout = tmp_session
745 .type_to_type_layout(&ty)
746 .map_err(|e| convert_vm_error(e, vm, tmp_session.get_resolver()))?;
747 let Some(bytes) = value.simple_serialize(&layout) else {
748 invariant_violation!("Failed to deserialize already serialized Move value");
749 };
750 let move_object = unsafe {
752 create_written_object(
753 vm,
754 &tmp_session,
755 protocol_config,
756 &input_object_metadata,
757 &loaded_child_objects,
758 id,
759 ty,
760 has_public_transfer,
761 bytes,
762 write_kind,
763 )?
764 };
765 let object = Object::new_move(move_object, recipient, tx_digest);
766 let change = ObjectChange::Write(object, write_kind);
767 object_changes.insert(id, change);
768 }
769 for (id, delete_kind) in deletions {
770 let delete_kind_with_seq = match delete_kind {
776 DeleteKind::Normal | DeleteKind::Wrap => {
777 let old_version = match input_object_metadata.get(&id) {
778 Some(metadata) => {
779 assert_invariant!(
780 !matches!(metadata, InputObjectMetadata::InputObject { owner: Owner::Immutable, .. }),
781 "Attempting to delete immutable object {id} via delete kind {delete_kind}"
782 );
783 metadata.version()
784 }
785 None => {
786 match loaded_child_objects.get(&id) {
787 Some(version) => *version,
788 None => invariant_violation!("Deleted/wrapped object {id} must be either in input or loaded child objects")
789 }
790 }
791 };
792 if delete_kind == DeleteKind::Normal {
793 DeleteKindWithOldVersion::Normal(old_version)
794 } else {
795 DeleteKindWithOldVersion::Wrap(old_version)
796 }
797 }
798 DeleteKind::UnwrapThenDelete => {
799 if protocol_config.simplified_unwrap_then_delete() {
800 DeleteKindWithOldVersion::UnwrapThenDelete
801 } else {
802 let old_version =
803 match state_view.get_latest_parent_entry_ref_deprecated(id) {
804 Some((_, previous_version, _)) => previous_version,
805 None => continue,
808 };
809 DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(old_version)
810 }
811 }
812 };
813 object_changes.insert(id, ObjectChange::Delete(delete_kind_with_seq));
814 }
815
816 let (res, linkage) = tmp_session.finish();
817 let change_set = res.map_err(|e| convert_vm_error(e, vm, &linkage))?;
818
819 assert_invariant!(change_set.accounts().is_empty(), "Change set must be empty");
822
823 Ok(ExecutionResults::V1(ExecutionResultsV1 {
824 object_changes,
825 user_events: user_events
826 .into_iter()
827 .map(|(module_id, tag, contents)| {
828 Event::new(
829 module_id.address(),
830 module_id.name(),
831 tx_context.sender(),
832 tag,
833 contents,
834 )
835 })
836 .collect(),
837 }))
838 }
839
840 pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
842 crate::error::convert_vm_error(error, self.vm, self.session.get_resolver())
843 }
844
845 pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
847 use move_core_types::vm_status::StatusCode;
848 use sui_types::execution_status::TypeArgumentError;
849 match error.major_status() {
850 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
851 ExecutionErrorKind::TypeArityMismatch.into()
852 }
853 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
854 argument_idx: idx as TypeParameterIndex,
855 kind: TypeArgumentError::TypeNotFound,
856 }
857 .into(),
858 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
859 argument_idx: idx as TypeParameterIndex,
860 kind: TypeArgumentError::ConstraintNotSatisfied,
861 }
862 .into(),
863 _ => self.convert_vm_error(error),
864 }
865 }
866
867 fn arg_is_borrowed(&self, arg: &Argument) -> bool {
869 self.borrowed.contains_key(arg)
870 }
871
872 fn arg_is_mut_borrowed(&self, arg: &Argument) -> bool {
874 matches!(self.borrowed.get(arg), Some(true))
875 }
876
877 fn borrow_mut(
879 &mut self,
880 arg: Argument,
881 usage: UsageKind,
882 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
883 {
884 self.borrow_mut_impl(arg, Some(usage))
885 }
886
887 fn borrow_mut_impl(
890 &mut self,
891 arg: Argument,
892 update_last_usage: Option<UsageKind>,
893 ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
894 {
895 let (metadata, result_value) = match arg {
896 Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
897 Argument::Input(i) => {
898 let Some(input_value) = self.inputs.get_mut(i as usize) else {
899 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
900 };
901 (input_value.object_metadata.as_ref(), &mut input_value.inner)
902 }
903 Argument::Result(i) => {
904 let Some(command_result) = self.results.get_mut(i as usize) else {
905 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
906 };
907 if command_result.len() != 1 {
908 return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
909 }
910 (None, &mut command_result[0])
911 }
912 Argument::NestedResult(i, j) => {
913 let Some(command_result) = self.results.get_mut(i as usize) else {
914 return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
915 };
916 let Some(result_value) = command_result.get_mut(j as usize) else {
917 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
918 result_idx: i,
919 secondary_idx: j,
920 });
921 };
922 (None, result_value)
923 }
924 };
925 if let Some(usage) = update_last_usage {
926 result_value.last_usage_kind = Some(usage);
927 }
928 Ok((metadata, &mut result_value.value))
929 }
930 }
931
932 impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
933 fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
934 self.session
935 .get_type_tag(type_)
936 .map_err(|e| self.convert_vm_error(e))
937 }
938 }
939
940 pub(crate) fn new_session<'state, 'vm>(
941 vm: &'vm MoveVM,
942 linkage: LinkageView<'state>,
943 child_resolver: &'state dyn RuntimeObjectResolver,
944 input_objects: BTreeMap<ObjectID, object_runtime::InputObject>,
945 is_metered: bool,
946 protocol_config: &ProtocolConfig,
947 metrics: Arc<ExecutionMetrics>,
948 ) -> Session<'state, 'vm, LinkageView<'state>> {
949 vm.new_session_with_extensions(
950 linkage,
951 new_native_extensions(
952 child_resolver,
953 input_objects,
954 is_metered,
955 protocol_config,
956 metrics,
957 ),
958 )
959 }
960
961 pub(crate) fn new_session_for_linkage<'vm, 'state>(
963 vm: &'vm MoveVM,
964 linkage: LinkageView<'state>,
965 ) -> Session<'state, 'vm, LinkageView<'state>> {
966 vm.new_session(linkage)
967 }
968
969 pub fn set_linkage(
971 session: &mut Session<LinkageView>,
972 linkage: &MovePackage,
973 ) -> Result<AccountAddress, ExecutionError> {
974 session.get_resolver_mut().set_linkage(linkage)
975 }
976
977 pub fn reset_linkage(session: &mut Session<LinkageView>) {
980 session.get_resolver_mut().reset_linkage();
981 }
982
983 pub fn steal_linkage(session: &mut Session<LinkageView>) -> Option<SavedLinkage> {
984 session.get_resolver_mut().steal_linkage()
985 }
986
987 pub fn restore_linkage(
988 session: &mut Session<LinkageView>,
989 saved: Option<SavedLinkage>,
990 ) -> Result<(), ExecutionError> {
991 session.get_resolver_mut().restore_linkage(saved)
992 }
993
994 fn package_for_linkage(
997 session: &Session<LinkageView>,
998 package_id: ObjectID,
999 ) -> VMResult<PackageObject> {
1000 use move_binary_format::errors::PartialVMError;
1001 use move_core_types::vm_status::StatusCode;
1002
1003 match session.get_resolver().get_package_object(&package_id) {
1004 Ok(Some(package)) => Ok(package),
1005 Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1006 .with_message(format!("Cannot find link context {package_id} in store"))
1007 .finish(Location::Undefined)),
1008 Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1009 .with_message(format!(
1010 "Error loading link context {package_id} from store: {err}"
1011 ))
1012 .finish(Location::Undefined)),
1013 }
1014 }
1015
1016 pub fn load_type(session: &mut Session<LinkageView>, type_tag: &TypeTag) -> VMResult<Type> {
1019 use move_binary_format::errors::PartialVMError;
1020 use move_core_types::vm_status::StatusCode;
1021
1022 fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1023 Err(PartialVMError::new(code).finish(Location::Undefined))
1024 }
1025
1026 Ok(match type_tag {
1027 TypeTag::Bool => Type::Bool,
1028 TypeTag::U8 => Type::U8,
1029 TypeTag::U16 => Type::U16,
1030 TypeTag::U32 => Type::U32,
1031 TypeTag::U64 => Type::U64,
1032 TypeTag::U128 => Type::U128,
1033 TypeTag::U256 => Type::U256,
1034 TypeTag::Address => Type::Address,
1035 TypeTag::Signer => Type::Signer,
1036
1037 TypeTag::Vector(inner) => Type::Vector(Box::new(load_type(session, inner)?)),
1038 TypeTag::Struct(struct_tag) => {
1039 let StructTag {
1040 address,
1041 module,
1042 name,
1043 type_params,
1044 } = struct_tag.as_ref();
1045
1046 let defining_id = ObjectID::from_address(*address);
1048 let package = package_for_linkage(session, defining_id)?;
1049
1050 let original_address =
1053 set_linkage(session, package.move_package()).map_err(|e| {
1054 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1055 .with_message(e.to_string())
1056 .finish(Location::Undefined)
1057 })?;
1058
1059 let runtime_id = ModuleId::new(original_address, module.clone());
1060 let res = session.load_struct(&runtime_id, name);
1061 reset_linkage(session);
1062 let (idx, struct_type) = res?;
1063
1064 let type_param_constraints = struct_type.type_param_constraints();
1066 if type_param_constraints.len() != type_params.len() {
1067 return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1068 }
1069
1070 if type_params.is_empty() {
1071 Type::Datatype(idx)
1072 } else {
1073 let loaded_type_params = type_params
1074 .iter()
1075 .map(|type_param| load_type(session, type_param))
1076 .collect::<VMResult<Vec<_>>>()?;
1077
1078 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1080 let abilities = session.get_type_abilities(param)?;
1081 if !constraint.is_subset(abilities) {
1082 return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1083 }
1084 }
1085
1086 Type::DatatypeInstantiation(Box::new((idx, loaded_type_params)))
1087 }
1088 }
1089 })
1090 }
1091
1092 pub(crate) fn make_object_value<'vm, 'state>(
1093 vm: &'vm MoveVM,
1094 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1095 type_: MoveObjectType,
1096 has_public_transfer: bool,
1097 used_in_non_entry_move_call: bool,
1098 contents: &[u8],
1099 ) -> Result<ObjectValue, ExecutionError> {
1100 let contents = if type_.is_coin() {
1101 let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1102 invariant_violation!("Could not deserialize a coin")
1103 };
1104 ObjectContents::Coin(coin)
1105 } else {
1106 ObjectContents::Raw(contents.to_vec())
1107 };
1108
1109 let tag: StructTag = type_.into();
1110 let type_ = load_type(session, &TypeTag::Struct(Box::new(tag)))
1111 .map_err(|e| crate::error::convert_vm_error(e, vm, session.get_resolver()))?;
1112 Ok(ObjectValue {
1113 type_,
1114 has_public_transfer,
1115 used_in_non_entry_move_call,
1116 contents,
1117 })
1118 }
1119
1120 pub(crate) fn value_from_object<'vm, 'state>(
1121 vm: &'vm MoveVM,
1122 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1123 object: &Object,
1124 ) -> Result<ObjectValue, ExecutionError> {
1125 let ObjectInner {
1126 data: Data::Move(object),
1127 ..
1128 } = object.as_inner()
1129 else {
1130 invariant_violation!("Expected a Move object");
1131 };
1132
1133 let used_in_non_entry_move_call = false;
1134 make_object_value(
1135 vm,
1136 session,
1137 object.type_().clone(),
1138 object.has_public_transfer(),
1139 used_in_non_entry_move_call,
1140 object.contents(),
1141 )
1142 }
1143
1144 fn load_object<'vm, 'state>(
1146 vm: &'vm MoveVM,
1147 state_view: &'state dyn StorageView,
1148 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1149 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1150 override_as_immutable: bool,
1151 id: ObjectID,
1152 ) -> Result<InputValue, ExecutionError> {
1153 let Some(obj) = state_view.read_object(&id) else {
1154 invariant_violation!("Object {} does not exist yet", id);
1156 };
1157 assert_invariant!(
1159 !override_as_immutable || matches!(obj.owner, Owner::Shared { .. }),
1160 "override_as_immutable should only be set for shared objects"
1161 );
1162 let is_mutable_input = match obj.owner {
1163 Owner::AddressOwner(_) => true,
1164 Owner::Shared { .. } => !override_as_immutable,
1165 Owner::Immutable => false,
1166 Owner::ObjectOwner(_) => {
1167 invariant_violation!("ObjectOwner objects cannot be input")
1169 }
1170 Owner::ConsensusAddressOwner { .. } => {
1171 unimplemented!("ConsensusAddressOwner does not exist for this execution version")
1172 }
1173 Owner::Party { .. } => {
1174 unimplemented!("Party does not exist for this execution version")
1175 }
1176 };
1177 let owner = obj.owner.clone();
1178 let version = obj.version();
1179 let object_metadata = InputObjectMetadata::InputObject {
1180 id,
1181 is_mutable_input,
1182 owner: owner.clone(),
1183 version,
1184 };
1185 let obj_value = value_from_object(vm, session, obj)?;
1186 let contained_uids = {
1187 let fully_annotated_layout =
1188 session
1189 .type_to_fully_annotated_layout(&obj_value.type_)
1190 .map_err(|e| convert_vm_error(e, vm, session.get_resolver()))?;
1191 let mut bytes = vec![];
1192 obj_value.write_bcs_bytes(&mut bytes);
1193 match get_all_uids(&fully_annotated_layout, &bytes) {
1194 Err(e) => {
1195 invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1196 }
1197 Ok(uids) => uids,
1198 }
1199 };
1200 let runtime_input = object_runtime::InputObject {
1201 contained_uids,
1202 owner,
1203 version,
1204 };
1205 let prev = input_object_map.insert(id, runtime_input);
1206 assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1208 Ok(InputValue::new_object(object_metadata, obj_value))
1209 }
1210
1211 fn load_call_arg<'vm, 'state>(
1213 vm: &'vm MoveVM,
1214 state_view: &'state dyn StorageView,
1215 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1216 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1217 call_arg: CallArg,
1218 ) -> Result<InputValue, ExecutionError> {
1219 Ok(match call_arg {
1220 CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1221 CallArg::Object(obj_arg) => {
1222 load_object_arg(vm, state_view, session, input_object_map, obj_arg)?
1223 }
1224 CallArg::FundsWithdrawal(_) => unreachable!("Impossible to hit BalanceWithdraw in v0"),
1225 })
1226 }
1227
1228 fn load_object_arg<'vm, 'state>(
1230 vm: &'vm MoveVM,
1231 state_view: &'state dyn StorageView,
1232 session: &mut Session<'state, 'vm, LinkageView<'state>>,
1233 input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1234 obj_arg: ObjectArg,
1235 ) -> Result<InputValue, ExecutionError> {
1236 match obj_arg {
1237 ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1238 vm,
1239 state_view,
1240 session,
1241 input_object_map,
1242 false,
1243 id,
1244 ),
1245 ObjectArg::SharedObject { id, mutability, .. } => load_object(
1246 vm,
1247 state_view,
1248 session,
1249 input_object_map,
1250 !mutability.is_exclusive(),
1251 id,
1252 ),
1253 ObjectArg::Receiving(_) => unreachable!("Impossible to hit Receiving in v0"),
1254 }
1255 }
1256
1257 fn add_additional_write(
1259 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1260 owner: Owner,
1261 object_value: ObjectValue,
1262 ) -> Result<(), ExecutionError> {
1263 let ObjectValue {
1264 type_,
1265 has_public_transfer,
1266 contents,
1267 ..
1268 } = object_value;
1269 let bytes = match contents {
1270 ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1271 ObjectContents::Raw(bytes) => bytes,
1272 };
1273 let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
1274 ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
1275 })?;
1276 let additional_write = AdditionalWrite {
1277 recipient: owner,
1278 type_,
1279 has_public_transfer,
1280 bytes,
1281 };
1282 additional_writes.insert(object_id, additional_write);
1283 Ok(())
1284 }
1285
1286 fn refund_max_gas_budget(
1289 additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1290 gas_charger: &mut GasCharger,
1291 gas_id: ObjectID,
1292 ) -> Result<(), ExecutionError> {
1293 let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1294 invariant_violation!("Gas object cannot be wrapped or destroyed")
1295 };
1296 let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1297 invariant_violation!("Gas object must be a coin")
1298 };
1299 let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1300 return Err(ExecutionError::new_with_source(
1301 ExecutionErrorKind::CoinBalanceOverflow,
1302 "Gas coin too large after returning the max gas budget",
1303 ));
1304 };
1305 coin.balance = Balance::new(new_balance);
1306 *bytes = coin.to_bcs_bytes();
1307 Ok(())
1308 }
1309
1310 unsafe fn create_written_object<'vm, 'state>(
1316 vm: &'vm MoveVM,
1317 session: &Session<'state, 'vm, LinkageView<'state>>,
1318 protocol_config: &ProtocolConfig,
1319 input_object_metadata: &BTreeMap<ObjectID, InputObjectMetadata>,
1320 loaded_child_objects: &BTreeMap<ObjectID, SequenceNumber>,
1321 id: ObjectID,
1322 type_: Type,
1323 has_public_transfer: bool,
1324 contents: Vec<u8>,
1325 write_kind: WriteKind,
1326 ) -> Result<MoveObject, ExecutionError> {
1327 debug_assert_eq!(
1328 id,
1329 MoveObject::id_opt(&contents).expect("object contents should start with an id")
1330 );
1331 let metadata_opt = input_object_metadata.get(&id);
1332 let loaded_child_version_opt = loaded_child_objects.get(&id);
1333 assert_invariant!(
1334 metadata_opt.is_none() || loaded_child_version_opt.is_none(),
1335 "Loaded {id} as a child, but that object was an input object",
1336 );
1337
1338 let old_obj_ver = metadata_opt
1339 .map(|metadata| metadata.version())
1340 .or_else(|| loaded_child_version_opt.copied());
1341
1342 debug_assert!(
1343 (write_kind == WriteKind::Mutate) == old_obj_ver.is_some(),
1344 "Inconsistent state: write_kind: {write_kind:?}, old ver: {old_obj_ver:?}"
1345 );
1346
1347 let type_tag = session
1348 .get_type_tag(&type_)
1349 .map_err(|e| crate::error::convert_vm_error(e, vm, session.get_resolver()))?;
1350
1351 let struct_tag = match type_tag {
1352 TypeTag::Struct(inner) => *inner,
1353 _ => invariant_violation!("Non struct type for object"),
1354 };
1355 MoveObject::new_from_execution(
1356 struct_tag.into(),
1357 has_public_transfer,
1358 old_obj_ver.unwrap_or_default(),
1359 contents,
1360 protocol_config,
1361 false,
1362 )
1363 }
1364}