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