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