1pub use checked::*;
6
7#[iota_macros::with_checked_arithmetic]
8mod checked {
9 use std::{
10 collections::{BTreeMap, BTreeSet},
11 fmt,
12 sync::Arc,
13 };
14
15 use iota_move_natives::object_runtime::ObjectRuntime;
16 use iota_protocol_config::ProtocolConfig;
17 use iota_types::{
18 IOTA_FRAMEWORK_ADDRESS,
19 base_types::{
20 IotaAddress, MoveObjectType, ObjectID, RESOLVED_ASCII_STR, RESOLVED_STD_OPTION,
21 RESOLVED_UTF8_STR, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME, TxContext,
22 TxContextKind,
23 },
24 coin::Coin,
25 error::{ExecutionError, ExecutionErrorKind, command_argument_error},
26 execution_config_utils::to_binary_config,
27 execution_status::{CommandArgumentError, PackageUpgradeError},
28 id::{RESOLVED_IOTA_ID, UID},
29 metrics::LimitsMetrics,
30 move_package::{
31 MovePackage, UpgradeCap, UpgradePolicy, UpgradeReceipt, UpgradeTicket,
32 normalize_deserialized_modules,
33 },
34 storage::{PackageObject, get_package_objects},
35 transaction::{Argument, Command, ProgrammableMoveCall, ProgrammableTransaction},
36 transfer::RESOLVED_RECEIVING_STRUCT,
37 };
38 use iota_verifier::{
39 INIT_FN_NAME,
40 private_generics::{EVENT_MODULE, PRIVATE_TRANSFER_FUNCTIONS, TRANSFER_MODULE},
41 };
42 use move_binary_format::{
43 CompiledModule,
44 compatibility::{Compatibility, InclusionCheck},
45 errors::{Location, PartialVMResult, VMResult},
46 file_format::{AbilitySet, CodeOffset, FunctionDefinitionIndex, LocalIndex, Visibility},
47 file_format_common::VERSION_6,
48 normalized,
49 };
50 use move_core_types::{
51 account_address::AccountAddress,
52 identifier::IdentStr,
53 language_storage::{ModuleId, TypeTag},
54 u256::U256,
55 };
56 use move_trace_format::format::MoveTraceBuilder;
57 use move_vm_runtime::{
58 move_vm::MoveVM,
59 session::{LoadedFunctionInstantiation, SerializedReturnValues},
60 };
61 use move_vm_types::loaded_data::runtime_types::{CachedDatatype, Type};
62 use serde::{Deserialize, de::DeserializeSeed};
63 use tracing::instrument;
64
65 use crate::{
66 adapter::substitute_package_id,
67 execution_mode::ExecutionMode,
68 execution_value::{
69 CommandKind, ExecutionState, ObjectContents, ObjectValue, RawValueType, Value,
70 ensure_serialized_size,
71 },
72 gas_charger::GasCharger,
73 programmable_transactions::context::*,
74 };
75
76 pub fn execute<Mode: ExecutionMode>(
84 protocol_config: &ProtocolConfig,
85 metrics: Arc<LimitsMetrics>,
86 vm: &MoveVM,
87 state_view: &mut dyn ExecutionState,
88 tx_context: &mut TxContext,
89 gas_charger: &mut GasCharger,
90 pt: ProgrammableTransaction,
91 trace_builder_opt: &mut Option<MoveTraceBuilder>,
92 ) -> Result<Mode::ExecutionResults, ExecutionError> {
93 let ProgrammableTransaction { inputs, commands } = pt;
94 let mut context = ExecutionContext::new(
95 protocol_config,
96 metrics,
97 vm,
98 state_view,
99 tx_context,
100 gas_charger,
101 inputs,
102 )?;
103 let mut mode_results = Mode::empty_results();
105 for (idx, command) in commands.into_iter().enumerate() {
106 if let Err(err) =
107 execute_command::<Mode>(&mut context, &mut mode_results, command, trace_builder_opt)
108 {
109 let object_runtime: &ObjectRuntime = context.object_runtime();
110 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
112 drop(context);
115 state_view.save_loaded_runtime_objects(loaded_runtime_objects);
116 return Err(err.with_command_index(idx));
117 };
118 }
119
120 let object_runtime: &ObjectRuntime = context.object_runtime();
122 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
126 let wrapped_object_containers = object_runtime.wrapped_object_containers();
129
130 let finished = context.finish::<Mode>();
132 state_view.save_loaded_runtime_objects(loaded_runtime_objects);
134 state_view.save_wrapped_object_containers(wrapped_object_containers);
135 state_view.record_execution_results(finished?);
136 Ok(mode_results)
137 }
138
139 #[instrument(level = "trace", skip_all)]
141 fn execute_command<Mode: ExecutionMode>(
142 context: &mut ExecutionContext<'_, '_, '_>,
143 mode_results: &mut Mode::ExecutionResults,
144 command: Command,
145 trace_builder_opt: &mut Option<MoveTraceBuilder>,
146 ) -> Result<(), ExecutionError> {
147 let mut argument_updates = Mode::empty_arguments();
148 let results = match command {
149 Command::MakeMoveVec(tag_opt, args) if args.is_empty() => {
150 let Some(tag) = tag_opt else {
151 invariant_violation!(
152 "input checker ensures if args are empty, there is a type specified"
153 );
154 };
155 let elem_ty = context.load_type(&tag).map_err(|e| {
156 if context.protocol_config.convert_type_argument_error() {
157 context.convert_type_argument_error(0, e)
158 } else {
159 context.convert_vm_error(e)
160 }
161 })?;
162 let ty = Type::Vector(Box::new(elem_ty));
163 let abilities = context
164 .vm
165 .get_runtime()
166 .get_type_abilities(&ty)
167 .map_err(|e| context.convert_vm_error(e))?;
168 let bytes = bcs::to_bytes::<Vec<u8>>(&vec![]).unwrap();
170 vec![Value::Raw(
171 RawValueType::Loaded {
172 ty,
173 abilities,
174 used_in_non_entry_move_call: false,
175 },
176 bytes,
177 )]
178 }
179 Command::MakeMoveVec(tag_opt, args) => {
180 let mut res = vec![];
181 leb128::write::unsigned(&mut res, args.len() as u64).unwrap();
182 let mut arg_iter = args.into_iter().enumerate();
183 let (mut used_in_non_entry_move_call, elem_ty) = match tag_opt {
184 Some(tag) => {
185 let elem_ty = context.load_type(&tag).map_err(|e| {
186 if context.protocol_config.convert_type_argument_error() {
187 context.convert_type_argument_error(0, e)
188 } else {
189 context.convert_vm_error(e)
190 }
191 })?;
192 (false, elem_ty)
193 }
194 None => {
196 let (idx, arg) = arg_iter.next().unwrap();
198 let obj: ObjectValue =
199 context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
200 obj.write_bcs_bytes(
201 &mut res,
202 amplification_bound::<Mode>(context, &obj.type_)?,
203 )?;
204 (obj.used_in_non_entry_move_call, obj.type_)
205 }
206 };
207 for (idx, arg) in arg_iter {
208 let value: Value = context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
209 check_param_type::<Mode>(context, idx, &value, &elem_ty)?;
210 used_in_non_entry_move_call =
211 used_in_non_entry_move_call || value.was_used_in_non_entry_move_call();
212 value.write_bcs_bytes(
213 &mut res,
214 amplification_bound::<Mode>(context, &elem_ty)?,
215 )?;
216 }
217 let ty = Type::Vector(Box::new(elem_ty));
218 let abilities = context
219 .vm
220 .get_runtime()
221 .get_type_abilities(&ty)
222 .map_err(|e| context.convert_vm_error(e))?;
223 vec![Value::Raw(
224 RawValueType::Loaded {
225 ty,
226 abilities,
227 used_in_non_entry_move_call,
228 },
229 res,
230 )]
231 }
232 Command::TransferObjects(objs, addr_arg) => {
233 let objs: Vec<ObjectValue> = objs
234 .into_iter()
235 .enumerate()
236 .map(|(idx, arg)| context.by_value_arg(CommandKind::TransferObjects, idx, arg))
237 .collect::<Result<_, _>>()?;
238 let addr: IotaAddress =
239 context.by_value_arg(CommandKind::TransferObjects, objs.len(), addr_arg)?;
240 for obj in objs {
241 obj.ensure_public_transfer_eligible()?;
242 context.transfer_object(obj, addr)?;
243 }
244 vec![]
245 }
246 Command::SplitCoins(coin_arg, amount_args) => {
247 let mut obj: ObjectValue = context.borrow_arg_mut(0, coin_arg)?;
248 let ObjectContents::Coin(coin) = &mut obj.contents else {
249 let e = ExecutionErrorKind::command_argument_error(
250 CommandArgumentError::TypeMismatch,
251 0,
252 );
253 let msg = "Expected a coin but got an non coin object".to_owned();
254 return Err(ExecutionError::new_with_source(e, msg));
255 };
256 let split_coins = amount_args
257 .into_iter()
258 .map(|amount_arg| {
259 let amount: u64 =
260 context.by_value_arg(CommandKind::SplitCoins, 1, amount_arg)?;
261 let new_coin_id = context.fresh_id()?;
262 let new_coin = coin.split(amount, UID::new(new_coin_id))?;
263 let coin_type = obj.type_.clone();
264 let new_coin = unsafe { ObjectValue::coin(coin_type, new_coin) };
268 Ok(Value::Object(new_coin))
269 })
270 .collect::<Result<_, ExecutionError>>()?;
271 context.restore_arg::<Mode>(&mut argument_updates, coin_arg, Value::Object(obj))?;
272 split_coins
273 }
274 Command::MergeCoins(target_arg, coin_args) => {
275 let mut target: ObjectValue = context.borrow_arg_mut(0, target_arg)?;
276 let ObjectContents::Coin(target_coin) = &mut target.contents else {
277 let e = ExecutionErrorKind::command_argument_error(
278 CommandArgumentError::TypeMismatch,
279 0,
280 );
281 let msg = "Expected a coin but got an non coin object".to_owned();
282 return Err(ExecutionError::new_with_source(e, msg));
283 };
284 let coins: Vec<ObjectValue> = coin_args
285 .into_iter()
286 .enumerate()
287 .map(|(idx, arg)| context.by_value_arg(CommandKind::MergeCoins, idx + 1, arg))
288 .collect::<Result<_, _>>()?;
289 for (idx, coin) in coins.into_iter().enumerate() {
290 if target.type_ != coin.type_ {
291 let e = ExecutionErrorKind::command_argument_error(
292 CommandArgumentError::TypeMismatch,
293 (idx + 1) as u16,
294 );
295 let msg = "Coins do not have the same type".to_owned();
296 return Err(ExecutionError::new_with_source(e, msg));
297 }
298 let ObjectContents::Coin(Coin { id, balance }) = coin.contents else {
299 invariant_violation!(
300 "Target coin was a coin, and we already checked for the same type. \
301 This should be a coin"
302 );
303 };
304 context.delete_id(*id.object_id())?;
305 target_coin.add(balance)?;
306 }
307 context.restore_arg::<Mode>(
308 &mut argument_updates,
309 target_arg,
310 Value::Object(target),
311 )?;
312 vec![]
313 }
314 Command::MoveCall(move_call) => {
315 let ProgrammableMoveCall {
316 package,
317 module,
318 function,
319 type_arguments,
320 arguments,
321 } = *move_call;
322
323 let mut loaded_type_arguments = Vec::with_capacity(type_arguments.len());
325 for (ix, type_arg) in type_arguments.into_iter().enumerate() {
326 let ty = context
327 .load_type(&type_arg)
328 .map_err(|e| context.convert_type_argument_error(ix, e))?;
329 loaded_type_arguments.push(ty);
330 }
331
332 let original_address = context.set_link_context(package)?;
333 let storage_id = ModuleId::new(*package, module.clone());
334 let runtime_id = ModuleId::new(original_address, module);
335 let return_values = execute_move_call::<Mode>(
336 context,
337 &mut argument_updates,
338 &storage_id,
339 &runtime_id,
340 &function,
341 loaded_type_arguments,
342 arguments,
343 false,
345 trace_builder_opt,
346 );
347
348 context.linkage_view.reset_linkage();
349 return_values?
350 }
351 Command::Publish(modules, dep_ids) => execute_move_publish::<Mode>(
352 context,
353 &mut argument_updates,
354 modules,
355 dep_ids,
356 trace_builder_opt,
357 )?,
358 Command::Upgrade(modules, dep_ids, current_package_id, upgrade_ticket) => {
359 execute_move_upgrade::<Mode>(
360 context,
361 modules,
362 dep_ids,
363 current_package_id,
364 upgrade_ticket,
365 )?
366 }
367 };
368
369 Mode::finish_command(context, mode_results, argument_updates, &results)?;
370 context.push_command_results(results)?;
371 Ok(())
372 }
373
374 fn execute_move_call<Mode: ExecutionMode>(
376 context: &mut ExecutionContext<'_, '_, '_>,
377 argument_updates: &mut Mode::ArgumentUpdates,
378 storage_id: &ModuleId,
379 runtime_id: &ModuleId,
380 function: &IdentStr,
381 type_arguments: Vec<Type>,
382 arguments: Vec<Argument>,
383 is_init: bool,
384 trace_builder_opt: &mut Option<MoveTraceBuilder>,
385 ) -> Result<Vec<Value>, ExecutionError> {
386 let LoadedFunctionInfo {
389 kind,
390 signature,
391 return_value_kinds,
392 index,
393 last_instr,
394 } = check_visibility_and_signature::<Mode>(
395 context,
396 runtime_id,
397 function,
398 &type_arguments,
399 is_init,
400 )?;
401 let (tx_context_kind, by_mut_ref, serialized_arguments) =
403 build_move_args::<Mode>(context, runtime_id, function, kind, &signature, &arguments)?;
404 let SerializedReturnValues {
406 mutable_reference_outputs,
407 return_values,
408 } = vm_move_call(
409 context,
410 runtime_id,
411 function,
412 type_arguments,
413 tx_context_kind,
414 serialized_arguments,
415 trace_builder_opt,
416 )?;
417 assert_invariant!(
418 by_mut_ref.len() == mutable_reference_outputs.len(),
419 "lost mutable input"
420 );
421
422 if context.protocol_config.relocate_event_module() {
423 context.take_user_events(storage_id, index, last_instr)?;
424 } else {
425 context.take_user_events(runtime_id, index, last_instr)?;
426 }
427
428 let saved_linkage = context.linkage_view.steal_linkage();
431 let used_in_non_entry_move_call = kind == FunctionKind::NonEntry;
435 let res = write_back_results::<Mode>(
436 context,
437 argument_updates,
438 &arguments,
439 used_in_non_entry_move_call,
440 mutable_reference_outputs
441 .into_iter()
442 .map(|(i, bytes, _layout)| (i, bytes)),
443 by_mut_ref,
444 return_values.into_iter().map(|(bytes, _layout)| bytes),
445 return_value_kinds,
446 );
447
448 context.linkage_view.restore_linkage(saved_linkage)?;
449 res
450 }
451
452 fn write_back_results<Mode: ExecutionMode>(
458 context: &mut ExecutionContext<'_, '_, '_>,
459 argument_updates: &mut Mode::ArgumentUpdates,
460 arguments: &[Argument],
461 non_entry_move_call: bool,
462 mut_ref_values: impl IntoIterator<Item = (u8, Vec<u8>)>,
463 mut_ref_kinds: impl IntoIterator<Item = (u8, ValueKind)>,
464 return_values: impl IntoIterator<Item = Vec<u8>>,
465 return_value_kinds: impl IntoIterator<Item = ValueKind>,
466 ) -> Result<Vec<Value>, ExecutionError> {
467 for ((i, bytes), (j, kind)) in mut_ref_values.into_iter().zip(mut_ref_kinds) {
468 assert_invariant!(i == j, "lost mutable input");
469 let arg_idx = i as usize;
470 let value = make_value(context, kind, bytes, non_entry_move_call)?;
471 context.restore_arg::<Mode>(argument_updates, arguments[arg_idx], value)?;
472 }
473
474 return_values
475 .into_iter()
476 .zip(return_value_kinds)
477 .map(|(bytes, kind)| {
478 make_value(
480 context, kind, bytes, true,
482 )
483 })
484 .collect()
485 }
486
487 fn make_value(
494 context: &mut ExecutionContext<'_, '_, '_>,
495 value_info: ValueKind,
496 bytes: Vec<u8>,
497 used_in_non_entry_move_call: bool,
498 ) -> Result<Value, ExecutionError> {
499 Ok(match value_info {
500 ValueKind::Object { type_, .. } => Value::Object(context.make_object_value(
501 type_,
502 used_in_non_entry_move_call,
503 &bytes,
504 )?),
505 ValueKind::Raw(ty, abilities) => Value::Raw(
506 RawValueType::Loaded {
507 ty,
508 abilities,
509 used_in_non_entry_move_call,
510 },
511 bytes,
512 ),
513 })
514 }
515
516 fn execute_move_publish<Mode: ExecutionMode>(
519 context: &mut ExecutionContext<'_, '_, '_>,
520 argument_updates: &mut Mode::ArgumentUpdates,
521 module_bytes: Vec<Vec<u8>>,
522 dep_ids: Vec<ObjectID>,
523 trace_builder_opt: &mut Option<MoveTraceBuilder>,
524 ) -> Result<Vec<Value>, ExecutionError> {
525 assert_invariant!(
526 !module_bytes.is_empty(),
527 "empty package is checked in transaction input checker"
528 );
529 context
530 .gas_charger
531 .charge_publish_package(module_bytes.iter().map(|v| v.len()).sum())?;
532
533 let mut modules = deserialize_modules::<Mode>(context, &module_bytes)?;
534
535 let runtime_id = if Mode::packages_are_predefined() {
539 (*modules[0].self_id().address()).into()
541 } else {
542 let id = context.tx_context.fresh_id();
543 substitute_package_id(&mut modules, id)?;
544 id
545 };
546
547 let storage_id = runtime_id;
549 let dependencies = fetch_packages(context, &dep_ids)?;
550 let package =
551 context.new_package(&modules, dependencies.iter().map(|p| p.move_package()))?;
552
553 context.linkage_view.set_linkage(&package)?;
560 context.write_package(package);
561 let res = publish_and_verify_modules(context, runtime_id, &modules).and_then(|_| {
562 init_modules::<Mode>(context, argument_updates, &modules, trace_builder_opt)
563 });
564 context.linkage_view.reset_linkage();
565 if res.is_err() {
566 context.pop_package();
567 }
568 res?;
569
570 let values = if Mode::packages_are_predefined() {
571 vec![]
573 } else {
574 let cap = &UpgradeCap::new(context.fresh_id()?, storage_id);
575 vec![Value::Object(context.make_object_value(
576 UpgradeCap::type_().into(),
577 false,
579 &bcs::to_bytes(cap).unwrap(),
580 )?)]
581 };
582 Ok(values)
583 }
584
585 fn execute_move_upgrade<Mode: ExecutionMode>(
588 context: &mut ExecutionContext<'_, '_, '_>,
589 module_bytes: Vec<Vec<u8>>,
590 dep_ids: Vec<ObjectID>,
591 current_package_id: ObjectID,
592 upgrade_ticket_arg: Argument,
593 ) -> Result<Vec<Value>, ExecutionError> {
594 assert_invariant!(
595 !module_bytes.is_empty(),
596 "empty package is checked in transaction input checker"
597 );
598 context
599 .gas_charger
600 .charge_upgrade_package(module_bytes.iter().map(|v| v.len()).sum())?;
601
602 let upgrade_ticket_type = context
603 .load_type_from_struct(&UpgradeTicket::type_())
604 .map_err(|e| context.convert_vm_error(e))?;
605 let upgrade_receipt_type = context
606 .load_type_from_struct(&UpgradeReceipt::type_())
607 .map_err(|e| context.convert_vm_error(e))?;
608
609 let upgrade_ticket: UpgradeTicket = {
610 let mut ticket_bytes = Vec::new();
611 let ticket_val: Value =
612 context.by_value_arg(CommandKind::Upgrade, 0, upgrade_ticket_arg)?;
613 check_param_type::<Mode>(context, 0, &ticket_val, &upgrade_ticket_type)?;
614 ticket_val.write_bcs_bytes(
615 &mut ticket_bytes,
616 amplification_bound::<Mode>(context, &upgrade_ticket_type)?,
617 )?;
618 bcs::from_bytes(&ticket_bytes).map_err(|_| {
619 ExecutionError::from_kind(ExecutionErrorKind::CommandArgumentError {
620 arg_idx: 0,
621 kind: CommandArgumentError::InvalidBCSBytes,
622 })
623 })?
624 };
625
626 if current_package_id != upgrade_ticket.package.bytes {
629 return Err(ExecutionError::from_kind(
630 ExecutionErrorKind::PackageUpgradeError {
631 upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
632 package_id: current_package_id,
633 ticket_id: upgrade_ticket.package.bytes,
634 },
635 },
636 ));
637 }
638
639 let computed_digest =
641 MovePackage::compute_digest_for_modules_and_deps(&module_bytes, &dep_ids).into();
642 if computed_digest != upgrade_ticket.digest {
643 return Err(ExecutionError::from_kind(
644 ExecutionErrorKind::PackageUpgradeError {
645 upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
646 digest: computed_digest,
647 },
648 },
649 ));
650 }
651
652 let current_package = fetch_package(context, &upgrade_ticket.package.bytes)?;
655
656 let mut modules = deserialize_modules::<Mode>(context, &module_bytes)?;
657 let runtime_id = current_package.move_package().original_package_id();
658 substitute_package_id(&mut modules, runtime_id)?;
659
660 let storage_id = context.tx_context.fresh_id();
663
664 let dependencies = fetch_packages(context, &dep_ids)?;
665 let package = context.upgrade_package(
666 storage_id,
667 current_package.move_package(),
668 &modules,
669 dependencies.iter().map(|p| p.move_package()),
670 )?;
671
672 context.linkage_view.set_linkage(&package)?;
673 let res = publish_and_verify_modules(context, runtime_id, &modules);
674 context.linkage_view.reset_linkage();
675 res?;
676
677 check_compatibility(
678 context,
679 current_package.move_package(),
680 &modules,
681 upgrade_ticket.policy,
682 )?;
683
684 context.write_package(package);
685 Ok(vec![Value::Raw(
686 RawValueType::Loaded {
687 ty: upgrade_receipt_type,
688 abilities: AbilitySet::EMPTY,
689 used_in_non_entry_move_call: false,
690 },
691 bcs::to_bytes(&UpgradeReceipt::new(upgrade_ticket, storage_id)).unwrap(),
692 )])
693 }
694
695 fn check_compatibility(
701 context: &ExecutionContext,
702 existing_package: &MovePackage,
703 upgrading_modules: &[CompiledModule],
704 policy: u8,
705 ) -> Result<(), ExecutionError> {
706 let Ok(policy) = UpgradePolicy::try_from(policy) else {
708 return Err(ExecutionError::from_kind(
709 ExecutionErrorKind::PackageUpgradeError {
710 upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
711 },
712 ));
713 };
714
715 let binary_config = to_binary_config(context.protocol_config);
716 let Ok(current_normalized) = existing_package.normalize(&binary_config) else {
717 invariant_violation!("Tried to normalize modules in existing package but failed")
718 };
719
720 let existing_modules_len = current_normalized.len();
721 let upgrading_modules_len = upgrading_modules.len();
722 let disallow_new_modules = context
723 .protocol_config
724 .disallow_new_modules_in_deps_only_packages()
725 && policy as u8 == UpgradePolicy::DEP_ONLY;
726 if disallow_new_modules && existing_modules_len != upgrading_modules_len {
727 return Err(ExecutionError::new_with_source(
728 ExecutionErrorKind::PackageUpgradeError {
729 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
730 },
731 format!(
732 "Existing package has {existing_modules_len} modules, but new package has \
733 {upgrading_modules_len}. Adding or removing a module to a deps only package is not allowed."
734 ),
735 ));
736 }
737 let mut new_normalized = normalize_deserialized_modules(upgrading_modules.iter());
738
739 for (name, cur_module) in current_normalized {
740 let Some(new_module) = new_normalized.remove(&name) else {
741 return Err(ExecutionError::new_with_source(
742 ExecutionErrorKind::PackageUpgradeError {
743 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
744 },
745 format!("Existing module {name} not found in next version of package"),
746 ));
747 };
748
749 check_module_compatibility(&policy, &cur_module, &new_module)?;
750 }
751
752 debug_assert!(!disallow_new_modules || new_normalized.is_empty());
755
756 Ok(())
757 }
758
759 fn check_module_compatibility(
765 policy: &UpgradePolicy,
766 cur_module: &normalized::Module,
767 new_module: &normalized::Module,
768 ) -> Result<(), ExecutionError> {
769 match policy {
770 UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
771 UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
772 UpgradePolicy::Compatible => {
773 let compatibility = Compatibility::upgrade_check();
774
775 compatibility.check(cur_module, new_module)
776 }
777 }
778 .map_err(|e| {
779 ExecutionError::new_with_source(
780 ExecutionErrorKind::PackageUpgradeError {
781 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
782 },
783 e,
784 )
785 })
786 }
787
788 fn fetch_package(
793 context: &ExecutionContext<'_, '_, '_>,
794 package_id: &ObjectID,
795 ) -> Result<PackageObject, ExecutionError> {
796 let mut fetched_packages = fetch_packages(context, vec![package_id])?;
797 assert_invariant!(
798 fetched_packages.len() == 1,
799 "Number of fetched packages must match the number of package object IDs if successful."
800 );
801 match fetched_packages.pop() {
802 Some(pkg) => Ok(pkg),
803 None => invariant_violation!(
804 "We should always fetch a package for each object or return a dependency error."
805 ),
806 }
807 }
808
809 fn fetch_packages<'ctx, 'vm, 'state, 'a>(
813 context: &'ctx ExecutionContext<'vm, 'state, 'a>,
814 package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
815 ) -> Result<Vec<PackageObject>, ExecutionError> {
816 let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
817 match get_package_objects(&context.state_view, package_ids) {
818 Err(e) => Err(ExecutionError::new_with_source(
819 ExecutionErrorKind::PublishUpgradeMissingDependency,
820 e,
821 )),
822 Ok(Err(missing_deps)) => {
823 let msg = format!(
824 "Missing dependencies: {}",
825 missing_deps
826 .into_iter()
827 .map(|dep| format!("{}", dep))
828 .collect::<Vec<_>>()
829 .join(", ")
830 );
831 Err(ExecutionError::new_with_source(
832 ExecutionErrorKind::PublishUpgradeMissingDependency,
833 msg,
834 ))
835 }
836 Ok(Ok(pkgs)) => Ok(pkgs),
837 }
838 }
839
840 fn vm_move_call(
851 context: &mut ExecutionContext<'_, '_, '_>,
852 module_id: &ModuleId,
853 function: &IdentStr,
854 type_arguments: Vec<Type>,
855 tx_context_kind: TxContextKind,
856 mut serialized_arguments: Vec<Vec<u8>>,
857 trace_builder_opt: &mut Option<MoveTraceBuilder>,
858 ) -> Result<SerializedReturnValues, ExecutionError> {
859 match tx_context_kind {
860 TxContextKind::None => (),
861 TxContextKind::Mutable | TxContextKind::Immutable => {
862 serialized_arguments.push(context.tx_context.to_vec());
863 }
864 }
865 let mut result = context
867 .execute_function_bypass_visibility(
868 module_id,
869 function,
870 type_arguments,
871 serialized_arguments,
872 trace_builder_opt,
873 )
874 .map_err(|e| context.convert_vm_error(e))?;
875
876 if tx_context_kind == TxContextKind::Mutable {
884 let Some((_, ctx_bytes, _)) = result.mutable_reference_outputs.pop() else {
885 invariant_violation!("Missing TxContext in reference outputs");
886 };
887 let updated_ctx: TxContext = bcs::from_bytes(&ctx_bytes).map_err(|e| {
888 ExecutionError::invariant_violation(format!(
889 "Unable to deserialize TxContext bytes. {e}"
890 ))
891 })?;
892 context.tx_context.update_state(updated_ctx)?;
893 }
894 Ok(result)
895 }
896
897 #[expect(clippy::extra_unused_type_parameters)]
902 fn deserialize_modules<Mode: ExecutionMode>(
903 context: &mut ExecutionContext<'_, '_, '_>,
904 module_bytes: &[Vec<u8>],
905 ) -> Result<Vec<CompiledModule>, ExecutionError> {
906 let binary_config = to_binary_config(context.protocol_config);
907 let modules = module_bytes
908 .iter()
909 .map(|b| {
910 CompiledModule::deserialize_with_config(b, &binary_config)
911 .map_err(|e| e.finish(Location::Undefined))
912 })
913 .collect::<VMResult<Vec<CompiledModule>>>()
914 .map_err(|e| context.convert_vm_error(e))?;
915
916 assert_invariant!(
917 !modules.is_empty(),
918 "input checker ensures package is not empty"
919 );
920
921 Ok(modules)
922 }
923
924 fn publish_and_verify_modules(
930 context: &mut ExecutionContext<'_, '_, '_>,
931 package_id: ObjectID,
932 modules: &[CompiledModule],
933 ) -> Result<(), ExecutionError> {
934 let binary_version = context.protocol_config.move_binary_format_version();
936 let new_module_bytes: Vec<_> = modules
937 .iter()
938 .map(|m| {
939 let mut bytes = Vec::new();
940 let version = if binary_version > VERSION_6 {
941 m.version
942 } else {
943 VERSION_6
944 };
945 m.serialize_with_version(version, &mut bytes).unwrap();
946 bytes
947 })
948 .collect();
949 context
950 .publish_module_bundle(new_module_bytes, AccountAddress::from(package_id))
951 .map_err(|e| context.convert_vm_error(e))?;
952
953 for module in modules {
955 iota_verifier::verifier::iota_verify_module_unmetered(module, &BTreeMap::new())?;
958 }
959
960 Ok(())
961 }
962
963 fn init_modules<Mode: ExecutionMode>(
969 context: &mut ExecutionContext<'_, '_, '_>,
970 argument_updates: &mut Mode::ArgumentUpdates,
971 modules: &[CompiledModule],
972 trace_builder_opt: &mut Option<MoveTraceBuilder>,
973 ) -> Result<(), ExecutionError> {
974 let modules_to_init = modules.iter().filter_map(|module| {
975 for fdef in &module.function_defs {
976 let fhandle = module.function_handle_at(fdef.function);
977 let fname = module.identifier_at(fhandle.name);
978 if fname == INIT_FN_NAME {
979 return Some(module.self_id());
980 }
981 }
982 None
983 });
984
985 for module_id in modules_to_init {
986 let return_values = execute_move_call::<Mode>(
987 context,
988 argument_updates,
989 &module_id,
993 &module_id,
994 INIT_FN_NAME,
995 vec![],
996 vec![],
997 true,
999 trace_builder_opt,
1000 )?;
1001
1002 assert_invariant!(
1003 return_values.is_empty(),
1004 "init should not have return values"
1005 )
1006 }
1007
1008 Ok(())
1009 }
1010
1011 #[derive(PartialEq, Eq, Clone, Copy)]
1018 enum FunctionKind {
1019 PrivateEntry,
1020 PublicEntry,
1021 NonEntry,
1022 Init,
1023 }
1024
1025 enum ValueKind {
1028 Object { type_: MoveObjectType },
1029 Raw(Type, AbilitySet),
1030 }
1031
1032 struct LoadedFunctionInfo {
1033 kind: FunctionKind,
1035 signature: LoadedFunctionInstantiation,
1037 return_value_kinds: Vec<ValueKind>,
1039 index: FunctionDefinitionIndex,
1041 last_instr: CodeOffset,
1044 }
1045
1046 fn check_visibility_and_signature<Mode: ExecutionMode>(
1051 context: &mut ExecutionContext<'_, '_, '_>,
1052 module_id: &ModuleId,
1053 function: &IdentStr,
1054 type_arguments: &[Type],
1055 from_init: bool,
1056 ) -> Result<LoadedFunctionInfo, ExecutionError> {
1057 if from_init {
1058 let result = context.load_function(module_id, function, type_arguments);
1059 assert_invariant!(
1060 result.is_ok(),
1061 "The modules init should be able to be loaded"
1062 );
1063 }
1064 let no_new_packages = vec![];
1065 let data_store = IotaDataStore::new(&context.linkage_view, &no_new_packages);
1066 let module = context
1067 .vm
1068 .get_runtime()
1069 .load_module(module_id, &data_store)
1070 .map_err(|e| context.convert_vm_error(e))?;
1071 let Some((index, fdef)) = module
1072 .function_defs
1073 .iter()
1074 .enumerate()
1075 .find(|(_index, fdef)| {
1076 module.identifier_at(module.function_handle_at(fdef.function).name) == function
1077 })
1078 else {
1079 return Err(ExecutionError::new_with_source(
1080 ExecutionErrorKind::FunctionNotFound,
1081 format!(
1082 "Could not resolve function '{}' in module {}",
1083 function, &module_id,
1084 ),
1085 ));
1086 };
1087
1088 if !from_init && function == INIT_FN_NAME {
1090 return Err(ExecutionError::new_with_source(
1091 ExecutionErrorKind::NonEntryFunctionInvoked,
1092 "Cannot call 'init'",
1093 ));
1094 }
1095
1096 let last_instr: CodeOffset = fdef
1097 .code
1098 .as_ref()
1099 .map(|code| code.code.len() - 1)
1100 .unwrap_or(0) as CodeOffset;
1101 let function_kind = match (fdef.visibility, fdef.is_entry) {
1102 (Visibility::Private | Visibility::Friend, true) => FunctionKind::PrivateEntry,
1103 (Visibility::Public, true) => FunctionKind::PublicEntry,
1104 (Visibility::Public, false) => FunctionKind::NonEntry,
1105 (Visibility::Private, false) if from_init => {
1106 assert_invariant!(
1107 function == INIT_FN_NAME,
1108 "module init specified non-init function"
1109 );
1110 FunctionKind::Init
1111 }
1112 (Visibility::Private | Visibility::Friend, false)
1113 if Mode::allow_arbitrary_function_calls() =>
1114 {
1115 FunctionKind::NonEntry
1116 }
1117 (Visibility::Private | Visibility::Friend, false) => {
1118 return Err(ExecutionError::new_with_source(
1119 ExecutionErrorKind::NonEntryFunctionInvoked,
1120 "Can only call `entry` or `public` functions",
1121 ));
1122 }
1123 };
1124 let signature = context
1125 .load_function(module_id, function, type_arguments)
1126 .map_err(|e| context.convert_vm_error(e))?;
1127 let signature =
1128 subst_signature(signature, type_arguments).map_err(|e| context.convert_vm_error(e))?;
1129 let return_value_kinds = match function_kind {
1130 FunctionKind::Init => {
1131 assert_invariant!(
1132 signature.return_.is_empty(),
1133 "init functions must have no return values"
1134 );
1135 vec![]
1136 }
1137 FunctionKind::PrivateEntry | FunctionKind::PublicEntry | FunctionKind::NonEntry => {
1138 check_non_entry_signature::<Mode>(context, module_id, function, &signature)?
1139 }
1140 };
1141 check_private_generics(context, module_id, function, type_arguments)?;
1142 Ok(LoadedFunctionInfo {
1143 kind: function_kind,
1144 signature,
1145 return_value_kinds,
1146 index: FunctionDefinitionIndex(index as u16),
1147 last_instr,
1148 })
1149 }
1150
1151 fn subst_signature(
1153 signature: LoadedFunctionInstantiation,
1154 type_arguments: &[Type],
1155 ) -> VMResult<LoadedFunctionInstantiation> {
1156 let LoadedFunctionInstantiation {
1157 parameters,
1158 return_,
1159 } = signature;
1160 let parameters = parameters
1161 .into_iter()
1162 .map(|ty| ty.subst(type_arguments))
1163 .collect::<PartialVMResult<Vec<_>>>()
1164 .map_err(|err| err.finish(Location::Undefined))?;
1165 let return_ = return_
1166 .into_iter()
1167 .map(|ty| ty.subst(type_arguments))
1168 .collect::<PartialVMResult<Vec<_>>>()
1169 .map_err(|err| err.finish(Location::Undefined))?;
1170 Ok(LoadedFunctionInstantiation {
1171 parameters,
1172 return_,
1173 })
1174 }
1175
1176 fn check_non_entry_signature<Mode: ExecutionMode>(
1179 context: &mut ExecutionContext<'_, '_, '_>,
1180 _module_id: &ModuleId,
1181 _function: &IdentStr,
1182 signature: &LoadedFunctionInstantiation,
1183 ) -> Result<Vec<ValueKind>, ExecutionError> {
1184 signature
1185 .return_
1186 .iter()
1187 .enumerate()
1188 .map(|(idx, return_type)| {
1189 let return_type = match return_type {
1190 Type::Reference(inner) | Type::MutableReference(inner)
1192 if Mode::allow_arbitrary_values() =>
1193 {
1194 inner
1195 }
1196 Type::Reference(_) | Type::MutableReference(_) => {
1197 return Err(ExecutionError::from_kind(
1198 ExecutionErrorKind::InvalidPublicFunctionReturnType { idx: idx as u16 },
1199 ));
1200 }
1201 t => t,
1202 };
1203 let abilities = context
1204 .vm
1205 .get_runtime()
1206 .get_type_abilities(return_type)
1207 .map_err(|e| context.convert_vm_error(e))?;
1208 Ok(match return_type {
1209 Type::MutableReference(_) | Type::Reference(_) => unreachable!(),
1210 Type::TyParam(_) => {
1211 invariant_violation!("TyParam should have been substituted")
1212 }
1213 Type::Datatype(_) | Type::DatatypeInstantiation(_) if abilities.has_key() => {
1214 let type_tag = context
1215 .vm
1216 .get_runtime()
1217 .get_type_tag(return_type)
1218 .map_err(|e| context.convert_vm_error(e))?;
1219 let TypeTag::Struct(struct_tag) = type_tag else {
1220 invariant_violation!("Struct type make a non struct type tag")
1221 };
1222 ValueKind::Object {
1223 type_: MoveObjectType::from(*struct_tag),
1224 }
1225 }
1226 Type::Datatype(_)
1227 | Type::DatatypeInstantiation(_)
1228 | Type::Bool
1229 | Type::U8
1230 | Type::U64
1231 | Type::U128
1232 | Type::Address
1233 | Type::Signer
1234 | Type::Vector(_)
1235 | Type::U16
1236 | Type::U32
1237 | Type::U256 => ValueKind::Raw(return_type.clone(), abilities),
1238 })
1239 })
1240 .collect()
1241 }
1242
1243 fn check_private_generics(
1248 _context: &mut ExecutionContext,
1249 module_id: &ModuleId,
1250 function: &IdentStr,
1251 _type_arguments: &[Type],
1252 ) -> Result<(), ExecutionError> {
1253 let module_ident = (module_id.address(), module_id.name());
1254 if module_ident == (&IOTA_FRAMEWORK_ADDRESS, EVENT_MODULE) {
1255 return Err(ExecutionError::new_with_source(
1256 ExecutionErrorKind::NonEntryFunctionInvoked,
1257 format!("Cannot directly call functions in iota::{}", EVENT_MODULE),
1258 ));
1259 }
1260
1261 if module_ident == (&IOTA_FRAMEWORK_ADDRESS, TRANSFER_MODULE)
1262 && PRIVATE_TRANSFER_FUNCTIONS.contains(&function)
1263 {
1264 let msg = format!(
1265 "Cannot directly call iota::{m}::{f}. \
1266 Use the public variant instead, iota::{m}::public_{f}",
1267 m = TRANSFER_MODULE,
1268 f = function
1269 );
1270 return Err(ExecutionError::new_with_source(
1271 ExecutionErrorKind::NonEntryFunctionInvoked,
1272 msg,
1273 ));
1274 }
1275
1276 Ok(())
1277 }
1278
1279 type ArgInfo = (
1280 TxContextKind,
1281 Vec<(LocalIndex, ValueKind)>,
1283 Vec<Vec<u8>>,
1284 );
1285
1286 fn build_move_args<Mode: ExecutionMode>(
1289 context: &mut ExecutionContext<'_, '_, '_>,
1290 module_id: &ModuleId,
1291 function: &IdentStr,
1292 function_kind: FunctionKind,
1293 signature: &LoadedFunctionInstantiation,
1294 args: &[Argument],
1295 ) -> Result<ArgInfo, ExecutionError> {
1296 let parameters = &signature.parameters;
1298 let tx_ctx_kind = match parameters.last() {
1299 Some(t) => is_tx_context(context, t)?,
1300 None => TxContextKind::None,
1301 };
1302 let has_one_time_witness = function_kind == FunctionKind::Init && parameters.len() == 2;
1307 let has_tx_context = tx_ctx_kind != TxContextKind::None;
1308 let num_args = args.len() + (has_one_time_witness as usize) + (has_tx_context as usize);
1309 if num_args != parameters.len() {
1310 return Err(ExecutionError::new_with_source(
1311 ExecutionErrorKind::ArityMismatch,
1312 format!(
1313 "Expected {:?} argument{} calling function '{}', but found {:?}",
1314 parameters.len(),
1315 if parameters.len() == 1 { "" } else { "s" },
1316 function,
1317 num_args
1318 ),
1319 ));
1320 }
1321
1322 let mut by_mut_ref = vec![];
1324 let mut serialized_args = Vec::with_capacity(num_args);
1325 let command_kind = CommandKind::MoveCall {
1326 package: (*module_id.address()).into(),
1327 module: module_id.name(),
1328 function,
1329 };
1330 if has_one_time_witness {
1335 let bcs_true_value = bcs::to_bytes(&true).unwrap();
1338 serialized_args.push(bcs_true_value)
1339 }
1340 for ((idx, arg), param_ty) in args.iter().copied().enumerate().zip(parameters) {
1341 let (value, non_ref_param_ty): (Value, &Type) = match param_ty {
1342 Type::MutableReference(inner) => {
1343 let value = context.borrow_arg_mut(idx, arg)?;
1344 let object_info = if let Value::Object(ObjectValue { type_, .. }) = &value {
1345 let type_tag = context
1346 .vm
1347 .get_runtime()
1348 .get_type_tag(type_)
1349 .map_err(|e| context.convert_vm_error(e))?;
1350 let TypeTag::Struct(struct_tag) = type_tag else {
1351 invariant_violation!("Struct type make a non struct type tag")
1352 };
1353 let type_ = (*struct_tag).into();
1354 ValueKind::Object { type_ }
1355 } else {
1356 let abilities = context
1357 .vm
1358 .get_runtime()
1359 .get_type_abilities(inner)
1360 .map_err(|e| context.convert_vm_error(e))?;
1361 ValueKind::Raw((**inner).clone(), abilities)
1362 };
1363 by_mut_ref.push((idx as LocalIndex, object_info));
1364 (value, inner)
1365 }
1366 Type::Reference(inner) => (context.borrow_arg(idx, arg, param_ty)?, inner),
1367 t => {
1368 let value = context.by_value_arg(command_kind, idx, arg)?;
1369 (value, t)
1370 }
1371 };
1372 if matches!(
1373 function_kind,
1374 FunctionKind::PrivateEntry | FunctionKind::Init
1375 ) && value.was_used_in_non_entry_move_call()
1376 {
1377 return Err(command_argument_error(
1378 CommandArgumentError::InvalidArgumentToPrivateEntryFunction,
1379 idx,
1380 ));
1381 }
1382 check_param_type::<Mode>(context, idx, &value, non_ref_param_ty)?;
1383 let bytes = {
1384 let mut v = vec![];
1385 value.write_bcs_bytes(&mut v, None)?;
1386 v
1387 };
1388 serialized_args.push(bytes);
1389 }
1390 Ok((tx_ctx_kind, by_mut_ref, serialized_args))
1391 }
1392
1393 fn check_param_type<Mode: ExecutionMode>(
1395 context: &mut ExecutionContext<'_, '_, '_>,
1396 idx: usize,
1397 value: &Value,
1398 param_ty: &Type,
1399 ) -> Result<(), ExecutionError> {
1400 match value {
1401 Value::Raw(RawValueType::Any, bytes) if Mode::allow_arbitrary_values() => {
1404 if let Some(bound) = amplification_bound::<Mode>(context, param_ty)? {
1405 return ensure_serialized_size(bytes.len() as u64, bound);
1406 } else {
1407 return Ok(());
1408 }
1409 }
1410 Value::Raw(RawValueType::Any, bytes) => {
1414 let Some(layout) = primitive_serialization_layout(context, param_ty)? else {
1415 let msg = format!(
1416 "Non-primitive argument at index {}. If it is an object, it must be \
1417 populated by an object",
1418 idx,
1419 );
1420 return Err(ExecutionError::new_with_source(
1421 ExecutionErrorKind::command_argument_error(
1422 CommandArgumentError::InvalidUsageOfPureArg,
1423 idx as u16,
1424 ),
1425 msg,
1426 ));
1427 };
1428 bcs_argument_validate(bytes, idx as u16, layout)?;
1429 return Ok(());
1430 }
1431 Value::Raw(RawValueType::Loaded { ty, abilities, .. }, _) => {
1432 assert_invariant!(
1433 Mode::allow_arbitrary_values() || !abilities.has_key(),
1434 "Raw value should never be an object"
1435 );
1436 if ty != param_ty {
1437 return Err(command_argument_error(
1438 CommandArgumentError::TypeMismatch,
1439 idx,
1440 ));
1441 }
1442 }
1443 Value::Object(obj) => {
1444 let ty = &obj.type_;
1445 if ty != param_ty {
1446 return Err(command_argument_error(
1447 CommandArgumentError::TypeMismatch,
1448 idx,
1449 ));
1450 }
1451 }
1452 Value::Receiving(_, _, assigned_type) => {
1453 if let Some(assigned_type) = assigned_type {
1455 if assigned_type != param_ty {
1456 return Err(command_argument_error(
1457 CommandArgumentError::TypeMismatch,
1458 idx,
1459 ));
1460 }
1461 }
1462
1463 let Type::DatatypeInstantiation(inst) = param_ty else {
1466 return Err(command_argument_error(
1467 CommandArgumentError::TypeMismatch,
1468 idx,
1469 ));
1470 };
1471 let (sidx, targs) = &**inst;
1472 let Some(s) = context.vm.get_runtime().get_type(*sidx) else {
1473 invariant_violation!("iota::transfer::Receiving struct not found in session")
1474 };
1475 let resolved_struct = get_datatype_ident(&s);
1476
1477 if resolved_struct != RESOLVED_RECEIVING_STRUCT || targs.len() != 1 {
1478 return Err(command_argument_error(
1479 CommandArgumentError::TypeMismatch,
1480 idx,
1481 ));
1482 }
1483 }
1484 }
1485 Ok(())
1486 }
1487
1488 fn get_datatype_ident(s: &CachedDatatype) -> (&AccountAddress, &IdentStr, &IdentStr) {
1489 let module_id = &s.defining_id;
1490 let struct_name = &s.name;
1491 (
1492 module_id.address(),
1493 module_id.name(),
1494 struct_name.as_ident_str(),
1495 )
1496 }
1497
1498 pub fn is_tx_context(
1502 context: &mut ExecutionContext<'_, '_, '_>,
1503 t: &Type,
1504 ) -> Result<TxContextKind, ExecutionError> {
1505 let (is_mut, inner) = match t {
1506 Type::MutableReference(inner) => (true, inner),
1507 Type::Reference(inner) => (false, inner),
1508 _ => return Ok(TxContextKind::None),
1509 };
1510 let Type::Datatype(idx) = &**inner else {
1511 return Ok(TxContextKind::None);
1512 };
1513 let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1514 invariant_violation!("Loaded struct not found")
1515 };
1516 let (module_addr, module_name, struct_name) = get_datatype_ident(&s);
1517 let is_tx_context_type = module_addr == &IOTA_FRAMEWORK_ADDRESS
1518 && module_name == TX_CONTEXT_MODULE_NAME
1519 && struct_name == TX_CONTEXT_STRUCT_NAME;
1520 Ok(if is_tx_context_type {
1521 if is_mut {
1522 TxContextKind::Mutable
1523 } else {
1524 TxContextKind::Immutable
1525 }
1526 } else {
1527 TxContextKind::None
1528 })
1529 }
1530
1531 fn primitive_serialization_layout(
1534 context: &mut ExecutionContext<'_, '_, '_>,
1535 param_ty: &Type,
1536 ) -> Result<Option<PrimitiveArgumentLayout>, ExecutionError> {
1537 Ok(match param_ty {
1538 Type::Signer => return Ok(None),
1539 Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => {
1540 invariant_violation!("references and type parameters should be checked elsewhere")
1541 }
1542 Type::Bool => Some(PrimitiveArgumentLayout::Bool),
1543 Type::U8 => Some(PrimitiveArgumentLayout::U8),
1544 Type::U16 => Some(PrimitiveArgumentLayout::U16),
1545 Type::U32 => Some(PrimitiveArgumentLayout::U32),
1546 Type::U64 => Some(PrimitiveArgumentLayout::U64),
1547 Type::U128 => Some(PrimitiveArgumentLayout::U128),
1548 Type::U256 => Some(PrimitiveArgumentLayout::U256),
1549 Type::Address => Some(PrimitiveArgumentLayout::Address),
1550
1551 Type::Vector(inner) => {
1552 let info_opt = primitive_serialization_layout(context, inner)?;
1553 info_opt.map(|layout| PrimitiveArgumentLayout::Vector(Box::new(layout)))
1554 }
1555 Type::DatatypeInstantiation(inst) => {
1556 let (idx, targs) = &**inst;
1557 let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1558 invariant_violation!("Loaded struct not found")
1559 };
1560 let resolved_struct = get_datatype_ident(&s);
1561 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
1563 let info_opt = primitive_serialization_layout(context, &targs[0])?;
1564 info_opt.map(|layout| PrimitiveArgumentLayout::Option(Box::new(layout)))
1565 } else {
1566 None
1567 }
1568 }
1569 Type::Datatype(idx) => {
1570 let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1571 invariant_violation!("Loaded struct not found")
1572 };
1573 let resolved_struct = get_datatype_ident(&s);
1574 if resolved_struct == RESOLVED_IOTA_ID {
1575 Some(PrimitiveArgumentLayout::Address)
1576 } else if resolved_struct == RESOLVED_ASCII_STR {
1577 Some(PrimitiveArgumentLayout::Ascii)
1578 } else if resolved_struct == RESOLVED_UTF8_STR {
1579 Some(PrimitiveArgumentLayout::UTF8)
1580 } else {
1581 None
1582 }
1583 }
1584 })
1585 }
1586
1587 fn amplification_bound<Mode: ExecutionMode>(
1588 context: &mut ExecutionContext<'_, '_, '_>,
1589 param_ty: &Type,
1590 ) -> Result<Option<u64>, ExecutionError> {
1591 if Mode::packages_are_predefined() {
1593 return Ok(None);
1594 }
1595
1596 let Some(bound) = context.protocol_config.max_ptb_value_size_as_option() else {
1597 return Ok(None);
1598 };
1599
1600 fn amplification(prim_layout: &PrimitiveArgumentLayout) -> Result<u64, ExecutionError> {
1601 use PrimitiveArgumentLayout as PAL;
1602 Ok(match prim_layout {
1603 PAL::Option(inner_layout) => 1u64 + amplification(inner_layout)?,
1604 PAL::Vector(inner_layout) => amplification(inner_layout)?,
1605 PAL::Ascii | PAL::UTF8 => 2,
1606 PAL::Bool | PAL::U8 | PAL::U16 | PAL::U32 | PAL::U64 => 1,
1607 PAL::U128 | PAL::U256 | PAL::Address => 2,
1608 })
1609 }
1610
1611 let mut amplification = match primitive_serialization_layout(context, param_ty)? {
1612 None => context.protocol_config.max_move_value_depth(),
1615 Some(layout) => amplification(&layout)?,
1616 };
1617
1618 debug_assert!(amplification != 0);
1620 debug_assert!(
1623 context.protocol_config.max_move_value_depth()
1624 >= context.protocol_config.max_type_argument_depth() as u64
1625 );
1626 assert_ne!(context.protocol_config.max_move_value_depth(), 0);
1627 if amplification == 0 {
1628 amplification = context.protocol_config.max_move_value_depth();
1629 }
1630 Ok(Some(bound / amplification))
1631 }
1632
1633 #[derive(Debug)]
1642 pub enum PrimitiveArgumentLayout {
1643 Option(Box<PrimitiveArgumentLayout>),
1645 Vector(Box<PrimitiveArgumentLayout>),
1647 Ascii,
1649 UTF8,
1651 Bool,
1653 U8,
1654 U16,
1655 U32,
1656 U64,
1657 U128,
1658 U256,
1659 Address,
1660 }
1661
1662 impl PrimitiveArgumentLayout {
1663 pub fn bcs_only(&self) -> bool {
1667 match self {
1668 PrimitiveArgumentLayout::Option(_)
1670 | PrimitiveArgumentLayout::Ascii
1671 | PrimitiveArgumentLayout::UTF8 => false,
1672 PrimitiveArgumentLayout::Bool
1674 | PrimitiveArgumentLayout::U8
1675 | PrimitiveArgumentLayout::U16
1676 | PrimitiveArgumentLayout::U32
1677 | PrimitiveArgumentLayout::U64
1678 | PrimitiveArgumentLayout::U128
1679 | PrimitiveArgumentLayout::U256
1680 | PrimitiveArgumentLayout::Address => true,
1681 PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
1683 }
1684 }
1685 }
1686
1687 pub fn bcs_argument_validate(
1693 bytes: &[u8],
1694 idx: u16,
1695 layout: PrimitiveArgumentLayout,
1696 ) -> Result<(), ExecutionError> {
1697 bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
1698 ExecutionError::new_with_source(
1699 ExecutionErrorKind::command_argument_error(
1700 CommandArgumentError::InvalidBCSBytes,
1701 idx,
1702 ),
1703 format!("Function expects {layout} but provided argument's value does not match",),
1704 )
1705 })
1706 }
1707
1708 impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
1709 type Value = ();
1710 fn deserialize<D: serde::de::Deserializer<'d>>(
1711 self,
1712 deserializer: D,
1713 ) -> Result<Self::Value, D::Error> {
1714 use serde::de::Error;
1715 match self {
1716 PrimitiveArgumentLayout::Ascii => {
1717 let s: &str = serde::Deserialize::deserialize(deserializer)?;
1718 if !s.is_ascii() {
1719 Err(D::Error::custom("not an ascii string"))
1720 } else {
1721 Ok(())
1722 }
1723 }
1724 PrimitiveArgumentLayout::UTF8 => {
1725 deserializer.deserialize_string(serde::de::IgnoredAny)?;
1726 Ok(())
1727 }
1728 PrimitiveArgumentLayout::Option(layout) => {
1729 deserializer.deserialize_option(OptionElementVisitor(layout))
1730 }
1731 PrimitiveArgumentLayout::Vector(layout) => {
1732 deserializer.deserialize_seq(VectorElementVisitor(layout))
1733 }
1734 PrimitiveArgumentLayout::Bool => {
1737 deserializer.deserialize_bool(serde::de::IgnoredAny)?;
1738 Ok(())
1739 }
1740 PrimitiveArgumentLayout::U8 => {
1741 deserializer.deserialize_u8(serde::de::IgnoredAny)?;
1742 Ok(())
1743 }
1744 PrimitiveArgumentLayout::U16 => {
1745 deserializer.deserialize_u16(serde::de::IgnoredAny)?;
1746 Ok(())
1747 }
1748 PrimitiveArgumentLayout::U32 => {
1749 deserializer.deserialize_u32(serde::de::IgnoredAny)?;
1750 Ok(())
1751 }
1752 PrimitiveArgumentLayout::U64 => {
1753 deserializer.deserialize_u64(serde::de::IgnoredAny)?;
1754 Ok(())
1755 }
1756 PrimitiveArgumentLayout::U128 => {
1757 deserializer.deserialize_u128(serde::de::IgnoredAny)?;
1758 Ok(())
1759 }
1760 PrimitiveArgumentLayout::U256 => {
1761 U256::deserialize(deserializer)?;
1762 Ok(())
1763 }
1764 PrimitiveArgumentLayout::Address => {
1765 IotaAddress::deserialize(deserializer)?;
1766 Ok(())
1767 }
1768 }
1769 }
1770 }
1771
1772 struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
1773
1774 impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
1775 type Value = ();
1776
1777 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1778 formatter.write_str("Vector")
1779 }
1780
1781 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1782 where
1783 A: serde::de::SeqAccess<'d>,
1784 {
1785 while seq.next_element_seed(self.0)?.is_some() {}
1786 Ok(())
1787 }
1788 }
1789
1790 struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
1791
1792 impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
1793 type Value = ();
1794
1795 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1796 formatter.write_str("Option")
1797 }
1798
1799 fn visit_none<E>(self) -> Result<Self::Value, E>
1800 where
1801 E: serde::de::Error,
1802 {
1803 Ok(())
1804 }
1805
1806 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1807 where
1808 D: serde::Deserializer<'d>,
1809 {
1810 self.0.deserialize(deserializer)
1811 }
1812 }
1813
1814 impl fmt::Display for PrimitiveArgumentLayout {
1815 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1816 match self {
1817 PrimitiveArgumentLayout::Vector(inner) => {
1818 write!(f, "vector<{inner}>")
1819 }
1820 PrimitiveArgumentLayout::Option(inner) => {
1821 write!(f, "std::option::Option<{inner}>")
1822 }
1823 PrimitiveArgumentLayout::Ascii => {
1824 write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
1825 }
1826 PrimitiveArgumentLayout::UTF8 => {
1827 write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
1828 }
1829 PrimitiveArgumentLayout::Bool => write!(f, "bool"),
1830 PrimitiveArgumentLayout::U8 => write!(f, "u8"),
1831 PrimitiveArgumentLayout::U16 => write!(f, "u16"),
1832 PrimitiveArgumentLayout::U32 => write!(f, "u32"),
1833 PrimitiveArgumentLayout::U64 => write!(f, "u64"),
1834 PrimitiveArgumentLayout::U128 => write!(f, "u128"),
1835 PrimitiveArgumentLayout::U256 => write!(f, "u256"),
1836 PrimitiveArgumentLayout::Address => write!(f, "address"),
1837 }
1838 }
1839 }
1840}