1use crate::{
9 data_store::{
10 PackageStore, cached_package_store::CachedPackageStore, linked_data_store::LinkedDataStore,
11 },
12 execution_value::ExecutionState,
13 programmable_transactions::execution::subst_signature,
14 static_programmable_transactions::{
15 linkage::{
16 analysis::LinkageAnalyzer,
17 resolved_linkage::{ResolvedLinkage, RootedLinkage},
18 },
19 loading::ast::{
20 self as L, Datatype, LoadedFunction, LoadedFunctionInstantiation, Type, Vector,
21 },
22 },
23};
24use move_binary_format::{
25 CompiledModule,
26 errors::{Location, PartialVMError, VMError},
27 file_format::{AbilitySet, TypeParameterIndex},
28};
29use move_core_types::{
30 annotated_value,
31 language_storage::{ModuleId, StructTag},
32 runtime_value::{self, MoveTypeLayout},
33 vm_status::StatusCode,
34};
35use move_vm_runtime::move_vm::MoveVM;
36use move_vm_types::{data_store::DataStore, loaded_data::runtime_types as vm_runtime_type};
37use std::{cell::OnceCell, rc::Rc, sync::Arc};
38use sui_protocol_config::ProtocolConfig;
39use sui_types::{
40 Identifier, TypeTag,
41 base_types::{ObjectID, TxContext},
42 error::{ExecutionError, ExecutionErrorKind},
43 execution_status::TypeArgumentError,
44 gas_coin::GasCoin,
45 move_package::{UpgradeCap, UpgradeReceipt, UpgradeTicket},
46 object::Object,
47 type_input::{StructInput, TypeInput},
48};
49
50pub struct Env<'pc, 'vm, 'state, 'linkage> {
51 pub protocol_config: &'pc ProtocolConfig,
52 pub vm: &'vm MoveVM,
53 pub state_view: &'state mut dyn ExecutionState,
54 pub linkable_store: &'linkage CachedPackageStore<'state>,
55 pub linkage_analysis: &'linkage LinkageAnalyzer,
56 gas_coin_type: OnceCell<Type>,
57 upgrade_ticket_type: OnceCell<Type>,
58 upgrade_receipt_type: OnceCell<Type>,
59 upgrade_cap_type: OnceCell<Type>,
60 tx_context_type: OnceCell<Type>,
61}
62
63macro_rules! get_or_init_ty {
64 ($env:expr, $ident:ident, $tag:expr) => {{
65 let env = $env;
66 if env.$ident.get().is_none() {
67 let tag = $tag;
68 let ty = env.load_type_from_struct(&tag)?;
69 env.$ident.set(ty.clone()).unwrap();
70 }
71 Ok(env.$ident.get().unwrap().clone())
72 }};
73}
74
75impl<'pc, 'vm, 'state, 'linkage> Env<'pc, 'vm, 'state, 'linkage> {
76 pub fn new(
77 protocol_config: &'pc ProtocolConfig,
78 vm: &'vm MoveVM,
79 state_view: &'state mut dyn ExecutionState,
80 linkable_store: &'linkage CachedPackageStore<'state>,
81 linkage_analysis: &'linkage LinkageAnalyzer,
82 ) -> Self {
83 Self {
84 protocol_config,
85 vm,
86 state_view,
87 linkable_store,
88 linkage_analysis,
89 gas_coin_type: OnceCell::new(),
90 upgrade_ticket_type: OnceCell::new(),
91 upgrade_receipt_type: OnceCell::new(),
92 upgrade_cap_type: OnceCell::new(),
93 tx_context_type: OnceCell::new(),
94 }
95 }
96
97 pub fn convert_linked_vm_error(&self, e: VMError, linkage: &RootedLinkage) -> ExecutionError {
98 convert_vm_error(e, self.vm, self.linkable_store, Some(linkage))
99 }
100
101 pub fn convert_vm_error(&self, e: VMError) -> ExecutionError {
102 convert_vm_error(e, self.vm, self.linkable_store, None)
103 }
104
105 pub fn convert_type_argument_error(
106 &self,
107 idx: usize,
108 e: VMError,
109 linkage: &RootedLinkage,
110 ) -> ExecutionError {
111 use move_core_types::vm_status::StatusCode;
112 match e.major_status() {
113 StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
114 ExecutionErrorKind::TypeArityMismatch.into()
115 }
116 StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
117 argument_idx: idx as TypeParameterIndex,
118 kind: TypeArgumentError::TypeNotFound,
119 }
120 .into(),
121 StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
122 argument_idx: idx as TypeParameterIndex,
123 kind: TypeArgumentError::ConstraintNotSatisfied,
124 }
125 .into(),
126 _ => self.convert_linked_vm_error(e, linkage),
127 }
128 }
129
130 pub fn module_definition(
131 &self,
132 module_id: &ModuleId,
133 linkage: &RootedLinkage,
134 ) -> Result<Arc<CompiledModule>, ExecutionError> {
135 let linked_data_store = LinkedDataStore::new(linkage, self.linkable_store);
136 self.vm
137 .get_runtime()
138 .load_module(module_id, &linked_data_store)
139 .map_err(|e| self.convert_linked_vm_error(e, linkage))
140 }
141
142 pub fn fully_annotated_layout(
143 &self,
144 ty: &Type,
145 ) -> Result<annotated_value::MoveTypeLayout, ExecutionError> {
146 let ty = self.load_vm_type_from_adapter_type(None, ty)?;
147 self.vm
148 .get_runtime()
149 .type_to_fully_annotated_layout(&ty)
150 .map_err(|e| self.convert_vm_error(e))
151 }
152
153 pub fn runtime_layout(
154 &self,
155 ty: &Type,
156 ) -> Result<runtime_value::MoveTypeLayout, ExecutionError> {
157 let ty = self.load_vm_type_from_adapter_type(None, ty)?;
158 self.vm
159 .get_runtime()
160 .type_to_type_layout(&ty)
161 .map_err(|e| self.convert_vm_error(e))
162 }
163
164 pub fn load_function(
165 &self,
166 package: ObjectID,
167 module: String,
168 function: String,
169 type_arguments: Vec<Type>,
170 linkage: RootedLinkage,
171 ) -> Result<LoadedFunction, ExecutionError> {
172 let Some(original_id) = linkage.resolved_linkage.resolve_to_original_id(&package) else {
173 invariant_violation!(
174 "Package ID {:?} is not found in linkage generated for that package",
175 package
176 );
177 };
178 let module = to_identifier(module)?;
179 let name = to_identifier(function)?;
180 let storage_id = ModuleId::new(package.into(), module.clone());
181 let runtime_id = ModuleId::new(original_id.into(), module);
182 let mut data_store = LinkedDataStore::new(&linkage, self.linkable_store);
183 let loaded_type_arguments = type_arguments
184 .iter()
185 .enumerate()
186 .map(|(idx, ty)| self.load_vm_type_argument_from_adapter_type(idx, ty))
187 .collect::<Result<Vec<_>, _>>()?;
188 let runtime_signature = self
189 .vm
190 .get_runtime()
191 .load_function(
192 &runtime_id,
193 name.as_ident_str(),
194 &loaded_type_arguments,
195 &mut data_store,
196 )
197 .map_err(|e| {
198 if e.major_status() == StatusCode::FUNCTION_RESOLUTION_FAILURE {
199 ExecutionError::new_with_source(
200 ExecutionErrorKind::FunctionNotFound,
201 format!(
202 "Could not resolve function '{}' in module {}",
203 name, &storage_id,
204 ),
205 )
206 } else {
207 self.convert_linked_vm_error(e, &linkage)
208 }
209 })?;
210 let runtime_signature = subst_signature(runtime_signature, &loaded_type_arguments)
211 .map_err(|e| self.convert_linked_vm_error(e, &linkage))?;
212 let parameters = runtime_signature
213 .parameters
214 .into_iter()
215 .map(|ty| self.adapter_type_from_vm_type(&ty))
216 .collect::<Result<Vec<_>, _>>()?;
217 let return_ = runtime_signature
218 .return_
219 .into_iter()
220 .map(|ty| self.adapter_type_from_vm_type(&ty))
221 .collect::<Result<Vec<_>, _>>()?;
222 let signature = LoadedFunctionInstantiation {
223 parameters,
224 return_,
225 };
226 Ok(LoadedFunction {
227 storage_id,
228 runtime_id,
229 name,
230 type_arguments,
231 signature,
232 linkage,
233 instruction_length: runtime_signature.instruction_length,
234 definition_index: runtime_signature.definition_index,
235 })
236 }
237
238 pub fn load_type_input(&self, idx: usize, ty: TypeInput) -> Result<Type, ExecutionError> {
239 let runtime_type = self.load_vm_type_from_type_input(idx, ty)?;
240 self.adapter_type_from_vm_type(&runtime_type)
241 }
242
243 pub fn load_type_from_struct(&self, tag: &StructTag) -> Result<Type, ExecutionError> {
245 let vm_type =
246 self.load_vm_type_from_type_tag(None, &TypeTag::Struct(Box::new(tag.clone())))?;
247 self.adapter_type_from_vm_type(&vm_type)
248 }
249
250 pub fn type_layout_for_struct(
251 &self,
252 tag: &StructTag,
253 ) -> Result<MoveTypeLayout, ExecutionError> {
254 let ty: Type = self.load_type_from_struct(tag)?;
255 self.runtime_layout(&ty)
256 }
257
258 pub fn gas_coin_type(&self) -> Result<Type, ExecutionError> {
259 get_or_init_ty!(self, gas_coin_type, GasCoin::type_())
260 }
261
262 pub fn upgrade_ticket_type(&self) -> Result<Type, ExecutionError> {
263 get_or_init_ty!(self, upgrade_ticket_type, UpgradeTicket::type_())
264 }
265
266 pub fn upgrade_receipt_type(&self) -> Result<Type, ExecutionError> {
267 get_or_init_ty!(self, upgrade_receipt_type, UpgradeReceipt::type_())
268 }
269
270 pub fn upgrade_cap_type(&self) -> Result<Type, ExecutionError> {
271 get_or_init_ty!(self, upgrade_cap_type, UpgradeCap::type_())
272 }
273
274 pub fn tx_context_type(&self) -> Result<Type, ExecutionError> {
275 get_or_init_ty!(self, tx_context_type, TxContext::type_())
276 }
277
278 pub fn vector_type(&self, element_type: Type) -> Result<Type, ExecutionError> {
279 let abilities = AbilitySet::polymorphic_abilities(
280 AbilitySet::VECTOR,
281 [false],
282 [element_type.abilities()],
283 )
284 .map_err(|e| {
285 ExecutionError::new_with_source(ExecutionErrorKind::VMInvariantViolation, e.to_string())
286 })?;
287 Ok(Type::Vector(Rc::new(L::Vector {
288 abilities,
289 element_type,
290 })))
291 }
292
293 pub fn read_object(&self, id: &ObjectID) -> Result<&Object, ExecutionError> {
294 let Some(obj) = self.state_view.read_object(id) else {
295 invariant_violation!("Object {:?} does not exist", id);
297 };
298 Ok(obj)
299 }
300
301 pub fn load_vm_type_argument_from_adapter_type(
303 &self,
304 idx: usize,
305 ty: &Type,
306 ) -> Result<vm_runtime_type::Type, ExecutionError> {
307 self.load_vm_type_from_adapter_type(Some(idx), ty)
308 }
309
310 fn load_vm_type_from_adapter_type(
311 &self,
312 type_arg_idx: Option<usize>,
313 ty: &Type,
314 ) -> Result<vm_runtime_type::Type, ExecutionError> {
315 let tag: TypeTag = ty.clone().try_into().map_err(|s| {
316 ExecutionError::new_with_source(ExecutionErrorKind::VMInvariantViolation, s)
317 })?;
318 self.load_vm_type_from_type_tag(type_arg_idx, &tag)
319 }
320
321 fn load_vm_type_from_type_tag(
323 &self,
324 type_arg_idx: Option<usize>,
325 tag: &TypeTag,
326 ) -> Result<vm_runtime_type::Type, ExecutionError> {
327 use vm_runtime_type as VMR;
328
329 fn load_type_tag(
330 env: &Env,
331 type_arg_idx: Option<usize>,
332 tag: &TypeTag,
333 ) -> Result<VMR::Type, ExecutionError> {
334 Ok(match tag {
335 TypeTag::Bool => VMR::Type::Bool,
336 TypeTag::U8 => VMR::Type::U8,
337 TypeTag::U16 => VMR::Type::U16,
338 TypeTag::U32 => VMR::Type::U32,
339 TypeTag::U64 => VMR::Type::U64,
340 TypeTag::U128 => VMR::Type::U128,
341 TypeTag::U256 => VMR::Type::U256,
342 TypeTag::Address => VMR::Type::Address,
343 TypeTag::Signer => VMR::Type::Signer,
344
345 TypeTag::Vector(inner) => {
346 VMR::Type::Vector(Box::new(load_type_tag(env, type_arg_idx, inner)?))
347 }
348 TypeTag::Struct(tag) => load_struct_tag(env, type_arg_idx, tag)?,
349 })
350 }
351
352 fn load_struct_tag(
353 env: &Env,
354 type_arg_idx: Option<usize>,
355 struct_tag: &StructTag,
356 ) -> Result<vm_runtime_type::Type, ExecutionError> {
357 fn execution_error(
358 env: &Env,
359 type_arg_idx: Option<usize>,
360 e: VMError,
361 linkage: &RootedLinkage,
362 ) -> ExecutionError {
363 if let Some(idx) = type_arg_idx {
364 env.convert_type_argument_error(idx, e, linkage)
365 } else {
366 env.convert_linked_vm_error(e, linkage)
367 }
368 }
369
370 fn verification_error(code: StatusCode) -> VMError {
371 PartialVMError::new(code).finish(Location::Undefined)
372 }
373
374 let StructTag {
375 address,
376 module,
377 name,
378 type_params,
379 } = struct_tag;
380
381 let tag_linkage =
382 ResolvedLinkage::type_linkage(&[(*address).into()], env.linkable_store)?;
383 let linkage = RootedLinkage::new(*address, tag_linkage);
384 let linked_store = LinkedDataStore::new(&linkage, env.linkable_store);
385
386 let original_id = linkage
387 .resolved_linkage
388 .resolve_to_original_id(&(*address).into())
389 .ok_or_else(|| {
390 make_invariant_violation!(
391 "StructTag {:?} is not found in linkage generated for that struct tag -- this shouldn't happen.",
392 struct_tag
393 )
394 })?;
395 let runtime_id = ModuleId::new(*original_id, module.clone());
396
397 let (idx, struct_type) = env
398 .vm
399 .get_runtime()
400 .load_type(&runtime_id, name, &linked_store)
401 .map_err(|e| execution_error(env, type_arg_idx, e, &linkage))?;
402
403 let type_param_constraints = struct_type.type_param_constraints();
404 if type_param_constraints.len() != type_params.len() {
405 return Err(execution_error(
406 env,
407 type_arg_idx,
408 verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH),
409 &linkage,
410 ));
411 }
412
413 if type_params.is_empty() {
414 Ok(VMR::Type::Datatype(idx))
415 } else {
416 let loaded_type_params = type_params
417 .iter()
418 .map(|type_param| load_type_tag(env, type_arg_idx, type_param))
419 .collect::<Result<Vec<_>, _>>()?;
420
421 for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
423 let abilities = env
424 .vm
425 .get_runtime()
426 .get_type_abilities(param)
427 .map_err(|e| execution_error(env, type_arg_idx, e, &linkage))?;
428 if !constraint.is_subset(abilities) {
429 return Err(execution_error(
430 env,
431 type_arg_idx,
432 verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED),
433 &linkage,
434 ));
435 }
436 }
437
438 Ok(VMR::Type::DatatypeInstantiation(Box::new((
439 idx,
440 loaded_type_params,
441 ))))
442 }
443 }
444
445 load_type_tag(self, type_arg_idx, tag)
446 }
447
448 fn adapter_type_from_vm_type(
450 &self,
451 vm_type: &vm_runtime_type::Type,
452 ) -> Result<Type, ExecutionError> {
453 use vm_runtime_type as VRT;
454
455 Ok(match vm_type {
456 VRT::Type::Bool => Type::Bool,
457 VRT::Type::U8 => Type::U8,
458 VRT::Type::U16 => Type::U16,
459 VRT::Type::U32 => Type::U32,
460 VRT::Type::U64 => Type::U64,
461 VRT::Type::U128 => Type::U128,
462 VRT::Type::U256 => Type::U256,
463 VRT::Type::Address => Type::Address,
464 VRT::Type::Signer => Type::Signer,
465
466 VRT::Type::Reference(ref_ty) => {
467 let inner_ty = self.adapter_type_from_vm_type(ref_ty)?;
468 Type::Reference(false, Rc::new(inner_ty))
469 }
470 VRT::Type::MutableReference(ref_ty) => {
471 let inner_ty = self.adapter_type_from_vm_type(ref_ty)?;
472 Type::Reference(true, Rc::new(inner_ty))
473 }
474
475 VRT::Type::Vector(inner) => {
476 let element_type = self.adapter_type_from_vm_type(inner)?;
477 let abilities = self
478 .vm
479 .get_runtime()
480 .get_type_abilities(vm_type)
481 .map_err(|e| self.convert_vm_error(e))?;
482 let vector_ty = Vector {
483 abilities,
484 element_type,
485 };
486 Type::Vector(Rc::new(vector_ty))
487 }
488 VRT::Type::Datatype(cached_type_index) => {
489 let runtime = self.vm.get_runtime();
490 let Some(cached_info) = runtime.get_type(*cached_type_index) else {
491 invariant_violation!(
492 "Unable to find cached type info for {:?}. This should not happen as we have \
493 a loaded VM type in-hand.",
494 vm_type
495 )
496 };
497 let datatype = Datatype {
498 abilities: cached_info.abilities,
499 module: cached_info.defining_id.clone(),
500 name: cached_info.name.clone(),
501 type_arguments: vec![],
502 };
503 Type::Datatype(Rc::new(datatype))
504 }
505 ty @ VRT::Type::DatatypeInstantiation(inst) => {
506 let (cached_type_index, type_arguments) = &**inst;
507 let runtime = self.vm.get_runtime();
508 let Some(cached_info) = runtime.get_type(*cached_type_index) else {
509 invariant_violation!(
510 "Unable to find cached type info for {:?}. This should not happen as we have \
511 a loaded VM type in-hand.",
512 vm_type
513 )
514 };
515
516 let abilities = runtime
517 .get_type_abilities(ty)
518 .map_err(|e| self.convert_vm_error(e))?;
519 let module = cached_info.defining_id.clone();
520 let name = cached_info.name.clone();
521 let type_arguments = type_arguments
522 .iter()
523 .map(|t| self.adapter_type_from_vm_type(t))
524 .collect::<Result<Vec<_>, _>>()?;
525
526 Type::Datatype(Rc::new(Datatype {
527 abilities,
528 module,
529 name,
530 type_arguments,
531 }))
532 }
533
534 VRT::Type::TyParam(_) => {
535 invariant_violation!(
536 "Unexpected type parameter in VM type: {:?}. This should not happen as we should \
537 have resolved all type parameters before this point.",
538 vm_type
539 );
540 }
541 })
542 }
543
544 fn load_vm_type_from_type_input(
548 &self,
549 type_arg_idx: usize,
550 ty: TypeInput,
551 ) -> Result<vm_runtime_type::Type, ExecutionError> {
552 fn to_type_tag_internal(
553 env: &Env,
554 type_arg_idx: usize,
555 ty: TypeInput,
556 ) -> Result<TypeTag, ExecutionError> {
557 Ok(match ty {
558 TypeInput::Bool => TypeTag::Bool,
559 TypeInput::U8 => TypeTag::U8,
560 TypeInput::U16 => TypeTag::U16,
561 TypeInput::U32 => TypeTag::U32,
562 TypeInput::U64 => TypeTag::U64,
563 TypeInput::U128 => TypeTag::U128,
564 TypeInput::U256 => TypeTag::U256,
565 TypeInput::Address => TypeTag::Address,
566 TypeInput::Signer => TypeTag::Signer,
567 TypeInput::Vector(type_input) => {
568 let inner = to_type_tag_internal(env, type_arg_idx, *type_input)?;
569 TypeTag::Vector(Box::new(inner))
570 }
571 TypeInput::Struct(struct_input) => {
572 let StructInput {
573 address,
574 module,
575 name,
576 type_params,
577 } = *struct_input;
578
579 let pkg = env
580 .linkable_store
581 .get_package(&address.into())
582 .ok()
583 .flatten()
584 .ok_or_else(|| {
585 ExecutionError::from_kind(ExecutionErrorKind::TypeArgumentError {
586 argument_idx: type_arg_idx as u16,
587 kind: TypeArgumentError::TypeNotFound,
588 })
589 })?;
590 let Some(resolved_address) = pkg
591 .type_origin_map()
592 .get(&(module.clone(), name.clone()))
593 .cloned()
594 else {
595 return Err(ExecutionError::from_kind(
596 ExecutionErrorKind::TypeArgumentError {
597 argument_idx: type_arg_idx as u16,
598 kind: TypeArgumentError::TypeNotFound,
599 },
600 ));
601 };
602
603 let module = to_identifier(module)?;
604 let name = to_identifier(name)?;
605 let tys = type_params
606 .into_iter()
607 .map(|tp| to_type_tag_internal(env, type_arg_idx, tp))
608 .collect::<Result<Vec<_>, _>>()?;
609 TypeTag::Struct(Box::new(StructTag {
610 address: *resolved_address,
611 module,
612 name,
613 type_params: tys,
614 }))
615 }
616 })
617 }
618 let tag = to_type_tag_internal(self, type_arg_idx, ty)?;
619 self.load_vm_type_from_type_tag(Some(type_arg_idx), &tag)
620 }
621}
622
623fn to_identifier(name: String) -> Result<Identifier, ExecutionError> {
624 Identifier::new(name).map_err(|e| {
625 ExecutionError::new_with_source(ExecutionErrorKind::VMInvariantViolation, e.to_string())
626 })
627}
628
629fn convert_vm_error(
630 error: VMError,
631 vm: &MoveVM,
632 store: &dyn PackageStore,
633 linkage: Option<&RootedLinkage>,
634) -> ExecutionError {
635 use crate::error::convert_vm_error_impl;
636 convert_vm_error_impl(
637 error,
638 &|id| {
639 debug_assert!(
640 linkage.is_some(),
641 "Linkage should be set anywhere where runtime errors may occur in order to resolve abort locations to package IDs"
642 );
643 linkage
644 .and_then(|linkage| LinkedDataStore::new(linkage, store).relocate(id).ok())
645 .unwrap_or_else(|| id.clone())
646 },
647 &|id, function| {
649 debug_assert!(
650 linkage.is_some(),
651 "Linkage should be set anywhere where runtime errors may occur in order to resolve abort locations to package IDs"
652 );
653 linkage.and_then(|linkage| {
654 let state_view = LinkedDataStore::new(linkage, store);
655 vm.load_module(id, state_view).ok().map(|module| {
656 let fdef = module.function_def_at(function);
657 let fhandle = module.function_handle_at(fdef.function);
658 module.identifier_at(fhandle.name).to_string()
659 })
660 })
661 },
662 )
663}