1pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8 use crate::execution_mode::ExecutionMode;
9 use crate::execution_value::{
10 CommandKind, ExecutionState, ObjectContents, ObjectValue, RawValueType, Value,
11 };
12 use crate::gas_charger::GasCharger;
13 use move_binary_format::{
14 compatibility::{Compatibility, InclusionCheck},
15 errors::{Location, PartialVMResult, VMResult},
16 file_format::{AbilitySet, CodeOffset, FunctionDefinitionIndex, LocalIndex, Visibility},
17 file_format_common::VERSION_6,
18 normalized, CompiledModule,
19 };
20 use move_core_types::{
21 account_address::AccountAddress,
22 identifier::{IdentStr, Identifier},
23 language_storage::{ModuleId, TypeTag},
24 u256::U256,
25 };
26 use move_vm_runtime::{
27 move_vm::MoveVM,
28 session::{LoadedFunctionInstantiation, SerializedReturnValues},
29 };
30 use move_vm_types::loaded_data::runtime_types::{CachedDatatype, Type};
31 use serde::{de::DeserializeSeed, Deserialize};
32 use std::{
33 collections::{BTreeMap, BTreeSet},
34 fmt,
35 sync::Arc,
36 };
37 use sui_move_natives::object_runtime::ObjectRuntime;
38 use sui_protocol_config::ProtocolConfig;
39 use sui_types::execution_status::{CommandArgumentError, PackageUpgradeError};
40 use sui_types::storage::{get_package_objects, PackageObject};
41 use sui_types::{
42 base_types::{
43 MoveLegacyTxContext, MoveObjectType, ObjectID, SuiAddress, TxContext, TxContextKind,
44 RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, TX_CONTEXT_MODULE_NAME,
45 TX_CONTEXT_STRUCT_NAME,
46 },
47 coin::Coin,
48 error::{command_argument_error, ExecutionError},
49 execution_status::ExecutionErrorKind,
50 id::RESOLVED_SUI_ID,
51 metrics::LimitsMetrics,
52 move_package::{
53 normalize_deserialized_modules, MovePackage, UpgradeCap, UpgradePolicy, UpgradeReceipt,
54 UpgradeTicket,
55 },
56 transaction::{Argument, Command, ProgrammableMoveCall, ProgrammableTransaction},
57 transfer::RESOLVED_RECEIVING_STRUCT,
58 SUI_FRAMEWORK_ADDRESS,
59 };
60 use sui_verifier::{
61 private_generics::{EVENT_MODULE, PRIVATE_TRANSFER_FUNCTIONS, TRANSFER_MODULE},
62 INIT_FN_NAME,
63 };
64 use tracing::instrument;
65
66 use crate::adapter::substitute_package_id;
67 use crate::programmable_transactions::context::*;
68
69 pub fn execute<Mode: ExecutionMode>(
70 protocol_config: &ProtocolConfig,
71 metrics: Arc<LimitsMetrics>,
72 vm: &MoveVM,
73 state_view: &mut dyn ExecutionState,
74 tx_context: &mut TxContext,
75 gas_charger: &mut GasCharger,
76 pt: ProgrammableTransaction,
77 ) -> Result<Mode::ExecutionResults, ExecutionError> {
78 let ProgrammableTransaction { inputs, commands } = pt;
79 let mut context = ExecutionContext::new(
80 protocol_config,
81 metrics,
82 vm,
83 state_view,
84 tx_context,
85 gas_charger,
86 inputs,
87 )?;
88 let mut mode_results = Mode::empty_results();
90 for (idx, command) in commands.into_iter().enumerate() {
91 if let Err(err) = execute_command::<Mode>(&mut context, &mut mode_results, command) {
92 let object_runtime: &ObjectRuntime = context.object_runtime();
93 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
95 drop(context);
97 state_view.save_loaded_runtime_objects(loaded_runtime_objects);
98 return Err(err.with_command_index(idx));
99 };
100 }
101
102 let object_runtime: &ObjectRuntime = context.object_runtime();
104 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
108 let wrapped_object_containers = object_runtime.wrapped_object_containers();
111
112 let finished = context.finish::<Mode>();
114 state_view.save_loaded_runtime_objects(loaded_runtime_objects);
116 state_view.save_wrapped_object_containers(wrapped_object_containers);
117 state_view.record_execution_results(finished?)?;
118 Ok(mode_results)
119 }
120
121 #[instrument(level = "trace", skip_all)]
123 fn execute_command<Mode: ExecutionMode>(
124 context: &mut ExecutionContext<'_, '_, '_>,
125 mode_results: &mut Mode::ExecutionResults,
126 command: Command,
127 ) -> Result<(), ExecutionError> {
128 let mut argument_updates = Mode::empty_arguments();
129 let results = match command {
130 Command::MakeMoveVec(tag_opt, args) if args.is_empty() => {
131 let Some(tag) = tag_opt else {
132 invariant_violation!(
133 "input checker ensures if args are empty, there is a type specified"
134 );
135 };
136
137 let tag = unsafe { tag.into_type_tag_unchecked() };
140
141 let elem_ty = context
142 .load_type(&tag)
143 .map_err(|e| context.convert_vm_error(e))?;
144 let ty = Type::Vector(Box::new(elem_ty));
145 let abilities = context
146 .vm
147 .get_runtime()
148 .get_type_abilities(&ty)
149 .map_err(|e| context.convert_vm_error(e))?;
150 let bytes = bcs::to_bytes::<Vec<u8>>(&vec![]).unwrap();
152 vec![Value::Raw(
153 RawValueType::Loaded {
154 ty,
155 abilities,
156 used_in_non_entry_move_call: false,
157 },
158 bytes,
159 )]
160 }
161 Command::MakeMoveVec(tag_opt, args) => {
162 let mut res = vec![];
163 leb128::write::unsigned(&mut res, args.len() as u64).unwrap();
164 let mut arg_iter = args.into_iter().enumerate();
165 let (mut used_in_non_entry_move_call, elem_ty) = match tag_opt {
166 Some(tag) => {
167 let tag = unsafe { tag.into_type_tag_unchecked() };
170
171 let elem_ty = context
172 .load_type(&tag)
173 .map_err(|e| context.convert_vm_error(e))?;
174 (false, elem_ty)
175 }
176 None => {
178 let (idx, arg) = arg_iter.next().unwrap();
180 let obj: ObjectValue =
181 context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
182 obj.write_bcs_bytes(&mut res);
183 (obj.used_in_non_entry_move_call, obj.type_)
184 }
185 };
186 for (idx, arg) in arg_iter {
187 let value: Value = context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
188 check_param_type::<Mode>(context, idx, &value, &elem_ty)?;
189 used_in_non_entry_move_call =
190 used_in_non_entry_move_call || value.was_used_in_non_entry_move_call();
191 value.write_bcs_bytes(&mut res);
192 }
193 let ty = Type::Vector(Box::new(elem_ty));
194 let abilities = context
195 .vm
196 .get_runtime()
197 .get_type_abilities(&ty)
198 .map_err(|e| context.convert_vm_error(e))?;
199 vec![Value::Raw(
200 RawValueType::Loaded {
201 ty,
202 abilities,
203 used_in_non_entry_move_call,
204 },
205 res,
206 )]
207 }
208 Command::TransferObjects(objs, addr_arg) => {
209 let objs: Vec<ObjectValue> = objs
210 .into_iter()
211 .enumerate()
212 .map(|(idx, arg)| context.by_value_arg(CommandKind::TransferObjects, idx, arg))
213 .collect::<Result<_, _>>()?;
214 let addr: SuiAddress =
215 context.by_value_arg(CommandKind::TransferObjects, objs.len(), addr_arg)?;
216 for obj in objs {
217 obj.ensure_public_transfer_eligible()?;
218 context.transfer_object(obj, addr)?;
219 }
220 vec![]
221 }
222 Command::SplitCoins(coin_arg, amount_args) => {
223 let mut obj: ObjectValue = context.borrow_arg_mut(0, coin_arg)?;
224 let ObjectContents::Coin(coin) = &mut obj.contents else {
225 let e = ExecutionErrorKind::command_argument_error(
226 CommandArgumentError::TypeMismatch,
227 0,
228 );
229 let msg = "Expected a coin but got an non coin object".to_owned();
230 return Err(ExecutionError::new_with_source(e, msg));
231 };
232 let split_coins = amount_args
233 .into_iter()
234 .map(|amount_arg| {
235 let amount: u64 =
236 context.by_value_arg(CommandKind::SplitCoins, 1, amount_arg)?;
237 let new_coin_id = context.fresh_id()?;
238 let new_coin = coin.split(amount, new_coin_id)?;
239 let coin_type = obj.type_.clone();
240 let new_coin = unsafe { ObjectValue::coin(coin_type, new_coin) };
243 Ok(Value::Object(new_coin))
244 })
245 .collect::<Result<_, ExecutionError>>()?;
246 context.restore_arg::<Mode>(&mut argument_updates, coin_arg, Value::Object(obj))?;
247 split_coins
248 }
249 Command::MergeCoins(target_arg, coin_args) => {
250 let mut target: ObjectValue = context.borrow_arg_mut(0, target_arg)?;
251 let ObjectContents::Coin(target_coin) = &mut target.contents else {
252 let e = ExecutionErrorKind::command_argument_error(
253 CommandArgumentError::TypeMismatch,
254 0,
255 );
256 let msg = "Expected a coin but got an non coin object".to_owned();
257 return Err(ExecutionError::new_with_source(e, msg));
258 };
259 let coins: Vec<ObjectValue> = coin_args
260 .into_iter()
261 .enumerate()
262 .map(|(idx, arg)| context.by_value_arg(CommandKind::MergeCoins, idx + 1, arg))
263 .collect::<Result<_, _>>()?;
264 for (idx, coin) in coins.into_iter().enumerate() {
265 if target.type_ != coin.type_ {
266 let e = ExecutionErrorKind::command_argument_error(
267 CommandArgumentError::TypeMismatch,
268 (idx + 1) as u16,
269 );
270 let msg = "Coins do not have the same type".to_owned();
271 return Err(ExecutionError::new_with_source(e, msg));
272 }
273 let ObjectContents::Coin(Coin { id, balance }) = coin.contents else {
274 invariant_violation!(
275 "Target coin was a coin, and we already checked for the same type. \
276 This should be a coin"
277 );
278 };
279 context.delete_id(*id.object_id())?;
280 target_coin.add(balance)?;
281 }
282 context.restore_arg::<Mode>(
283 &mut argument_updates,
284 target_arg,
285 Value::Object(target),
286 )?;
287 vec![]
288 }
289 Command::MoveCall(move_call) => {
290 let ProgrammableMoveCall {
291 package,
292 module,
293 function,
294 type_arguments,
295 arguments,
296 } = *move_call;
297
298 let module = unsafe { Identifier::new_unchecked(module) };
300 let function = unsafe { Identifier::new_unchecked(function) };
301
302 let mut loaded_type_arguments = Vec::with_capacity(type_arguments.len());
304 for (ix, type_arg) in type_arguments.into_iter().enumerate() {
305 let type_arg = unsafe { type_arg.into_type_tag_unchecked() };
308
309 let ty = context
310 .load_type(&type_arg)
311 .map_err(|e| context.convert_type_argument_error(ix, e))?;
312 loaded_type_arguments.push(ty);
313 }
314
315 let original_address = context.set_link_context(package)?;
316 let runtime_id = ModuleId::new(original_address, module);
317 let return_values = execute_move_call::<Mode>(
318 context,
319 &mut argument_updates,
320 &runtime_id,
321 &function,
322 loaded_type_arguments,
323 arguments,
324 false,
325 );
326
327 context.linkage_view.reset_linkage();
328 return_values?
329 }
330 Command::Publish(modules, dep_ids) => {
331 execute_move_publish::<Mode>(context, &mut argument_updates, modules, dep_ids)?
332 }
333 Command::Upgrade(modules, dep_ids, current_package_id, upgrade_ticket) => {
334 execute_move_upgrade::<Mode>(
335 context,
336 modules,
337 dep_ids,
338 current_package_id,
339 upgrade_ticket,
340 )?
341 }
342 };
343
344 Mode::finish_command(context, mode_results, argument_updates, &results)?;
345 context.push_command_results(results)?;
346 Ok(())
347 }
348
349 fn execute_move_call<Mode: ExecutionMode>(
351 context: &mut ExecutionContext<'_, '_, '_>,
352 argument_updates: &mut Mode::ArgumentUpdates,
353 module_id: &ModuleId,
354 function: &IdentStr,
355 type_arguments: Vec<Type>,
356 arguments: Vec<Argument>,
357 is_init: bool,
358 ) -> Result<Vec<Value>, ExecutionError> {
359 let LoadedFunctionInfo {
361 kind,
362 signature,
363 return_value_kinds,
364 index,
365 last_instr,
366 } = check_visibility_and_signature::<Mode>(
367 context,
368 module_id,
369 function,
370 &type_arguments,
371 is_init,
372 )?;
373 let (tx_context_kind, by_mut_ref, serialized_arguments) =
375 build_move_args::<Mode>(context, module_id, function, kind, &signature, &arguments)?;
376 let SerializedReturnValues {
378 mutable_reference_outputs,
379 return_values,
380 } = vm_move_call(
381 context,
382 module_id,
383 function,
384 type_arguments,
385 tx_context_kind,
386 serialized_arguments,
387 )?;
388 assert_invariant!(
389 by_mut_ref.len() == mutable_reference_outputs.len(),
390 "lost mutable input"
391 );
392
393 context.take_user_events(module_id, index, last_instr)?;
394
395 let saved_linkage = context.linkage_view.steal_linkage();
398 let used_in_non_entry_move_call = kind == FunctionKind::NonEntry;
401 let res = write_back_results::<Mode>(
402 context,
403 argument_updates,
404 &arguments,
405 used_in_non_entry_move_call,
406 mutable_reference_outputs
407 .into_iter()
408 .map(|(i, bytes, _layout)| (i, bytes)),
409 by_mut_ref,
410 return_values.into_iter().map(|(bytes, _layout)| bytes),
411 return_value_kinds,
412 );
413
414 context.linkage_view.restore_linkage(saved_linkage)?;
415 res
416 }
417
418 fn write_back_results<Mode: ExecutionMode>(
419 context: &mut ExecutionContext<'_, '_, '_>,
420 argument_updates: &mut Mode::ArgumentUpdates,
421 arguments: &[Argument],
422 non_entry_move_call: bool,
423 mut_ref_values: impl IntoIterator<Item = (u8, Vec<u8>)>,
424 mut_ref_kinds: impl IntoIterator<Item = (u8, ValueKind)>,
425 return_values: impl IntoIterator<Item = Vec<u8>>,
426 return_value_kinds: impl IntoIterator<Item = ValueKind>,
427 ) -> Result<Vec<Value>, ExecutionError> {
428 for ((i, bytes), (j, kind)) in mut_ref_values.into_iter().zip(mut_ref_kinds) {
429 assert_invariant!(i == j, "lost mutable input");
430 let arg_idx = i as usize;
431 let value = make_value(context, kind, bytes, non_entry_move_call)?;
432 context.restore_arg::<Mode>(argument_updates, arguments[arg_idx], value)?;
433 }
434
435 return_values
436 .into_iter()
437 .zip(return_value_kinds)
438 .map(|(bytes, kind)| {
439 make_value(
441 context, kind, bytes, true,
442 )
443 })
444 .collect()
445 }
446
447 fn make_value(
448 context: &mut ExecutionContext<'_, '_, '_>,
449 value_info: ValueKind,
450 bytes: Vec<u8>,
451 used_in_non_entry_move_call: bool,
452 ) -> Result<Value, ExecutionError> {
453 Ok(match value_info {
454 ValueKind::Object {
455 type_,
456 has_public_transfer,
457 } => Value::Object(context.make_object_value(
458 type_,
459 has_public_transfer,
460 used_in_non_entry_move_call,
461 &bytes,
462 )?),
463 ValueKind::Raw(ty, abilities) => Value::Raw(
464 RawValueType::Loaded {
465 ty,
466 abilities,
467 used_in_non_entry_move_call,
468 },
469 bytes,
470 ),
471 })
472 }
473
474 fn execute_move_publish<Mode: ExecutionMode>(
477 context: &mut ExecutionContext<'_, '_, '_>,
478 argument_updates: &mut Mode::ArgumentUpdates,
479 module_bytes: Vec<Vec<u8>>,
480 dep_ids: Vec<ObjectID>,
481 ) -> Result<Vec<Value>, ExecutionError> {
482 assert_invariant!(
483 !module_bytes.is_empty(),
484 "empty package is checked in transaction input checker"
485 );
486 context
487 .gas_charger
488 .charge_publish_package(module_bytes.iter().map(|v| v.len()).sum())?;
489
490 let mut modules = deserialize_modules::<Mode>(context, &module_bytes)?;
491
492 let runtime_id = if Mode::packages_are_predefined() {
496 (*modules[0].self_id().address()).into()
498 } else {
499 let id = context.tx_context.fresh_id();
500 substitute_package_id(&mut modules, id)?;
501 id
502 };
503
504 let storage_id = runtime_id;
506 let dependencies = fetch_packages(context, &dep_ids)?;
507 let package =
508 context.new_package(&modules, dependencies.iter().map(|p| p.move_package()))?;
509
510 context.linkage_view.set_linkage(&package)?;
516 context.write_package(package);
517 let res = publish_and_verify_modules(context, runtime_id, &modules)
518 .and_then(|_| init_modules::<Mode>(context, argument_updates, &modules));
519 context.linkage_view.reset_linkage();
520 if res.is_err() {
521 context.pop_package();
522 }
523 res?;
524
525 let values = if Mode::packages_are_predefined() {
526 vec![]
528 } else {
529 let cap = &UpgradeCap::new(context.fresh_id()?, storage_id);
530 vec![Value::Object(context.make_object_value(
531 UpgradeCap::type_().into(),
532 true,
533 false,
534 &bcs::to_bytes(cap).unwrap(),
535 )?)]
536 };
537 Ok(values)
538 }
539
540 fn execute_move_upgrade<Mode: ExecutionMode>(
542 context: &mut ExecutionContext<'_, '_, '_>,
543 module_bytes: Vec<Vec<u8>>,
544 dep_ids: Vec<ObjectID>,
545 current_package_id: ObjectID,
546 upgrade_ticket_arg: Argument,
547 ) -> Result<Vec<Value>, ExecutionError> {
548 assert_invariant!(
549 !module_bytes.is_empty(),
550 "empty package is checked in transaction input checker"
551 );
552 context
553 .gas_charger
554 .charge_upgrade_package(module_bytes.iter().map(|v| v.len()).sum())?;
555
556 let upgrade_ticket_type = context
557 .load_type_from_struct(&UpgradeTicket::type_())
558 .map_err(|e| context.convert_vm_error(e))?;
559 let upgrade_receipt_type = context
560 .load_type_from_struct(&UpgradeReceipt::type_())
561 .map_err(|e| context.convert_vm_error(e))?;
562
563 let upgrade_ticket: UpgradeTicket = {
564 let mut ticket_bytes = Vec::new();
565 let ticket_val: Value =
566 context.by_value_arg(CommandKind::Upgrade, 0, upgrade_ticket_arg)?;
567 check_param_type::<Mode>(context, 0, &ticket_val, &upgrade_ticket_type)?;
568 ticket_val.write_bcs_bytes(&mut ticket_bytes);
569 bcs::from_bytes(&ticket_bytes).map_err(|_| {
570 ExecutionError::from_kind(ExecutionErrorKind::CommandArgumentError {
571 arg_idx: 0,
572 kind: CommandArgumentError::InvalidBCSBytes,
573 })
574 })?
575 };
576
577 if current_package_id != upgrade_ticket.package.bytes {
579 return Err(ExecutionError::from_kind(
580 ExecutionErrorKind::PackageUpgradeError {
581 upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
582 package_id: current_package_id,
583 ticket_id: upgrade_ticket.package.bytes,
584 },
585 },
586 ));
587 }
588
589 let hash_modules = true;
591 let computed_digest =
592 MovePackage::compute_digest_for_modules_and_deps(&module_bytes, &dep_ids, hash_modules)
593 .to_vec();
594 if computed_digest != upgrade_ticket.digest {
595 return Err(ExecutionError::from_kind(
596 ExecutionErrorKind::PackageUpgradeError {
597 upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
598 digest: computed_digest,
599 },
600 },
601 ));
602 }
603
604 let current_package = fetch_package(context, &upgrade_ticket.package.bytes)?;
606
607 let mut modules = deserialize_modules::<Mode>(context, &module_bytes)?;
608 let runtime_id = current_package.move_package().original_package_id();
609 substitute_package_id(&mut modules, runtime_id)?;
610
611 let storage_id = context.tx_context.fresh_id();
613
614 let dependencies = fetch_packages(context, &dep_ids)?;
615 let package = context.upgrade_package(
616 storage_id,
617 current_package.move_package(),
618 &modules,
619 dependencies.iter().map(|p| p.move_package()),
620 )?;
621
622 context.linkage_view.set_linkage(&package)?;
623 let res = publish_and_verify_modules(context, runtime_id, &modules);
624 context.linkage_view.reset_linkage();
625 res?;
626
627 check_compatibility(
628 context,
629 current_package.move_package(),
630 &modules,
631 upgrade_ticket.policy,
632 )?;
633
634 context.write_package(package);
635 Ok(vec![Value::Raw(
636 RawValueType::Loaded {
637 ty: upgrade_receipt_type,
638 abilities: AbilitySet::EMPTY,
639 used_in_non_entry_move_call: false,
640 },
641 bcs::to_bytes(&UpgradeReceipt::new(upgrade_ticket, storage_id)).unwrap(),
642 )])
643 }
644
645 fn check_compatibility<'a>(
646 context: &ExecutionContext,
647 existing_package: &MovePackage,
648 upgrading_modules: impl IntoIterator<Item = &'a CompiledModule>,
649 policy: u8,
650 ) -> Result<(), ExecutionError> {
651 let Ok(policy) = UpgradePolicy::try_from(policy) else {
653 return Err(ExecutionError::from_kind(
654 ExecutionErrorKind::PackageUpgradeError {
655 upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
656 },
657 ));
658 };
659
660 let pool = &mut normalized::RcPool::new();
661 let binary_config = context.protocol_config.binary_config(None);
662 let Ok(current_normalized) =
663 existing_package.normalize(pool, &binary_config, true)
664 else {
665 invariant_violation!("Tried to normalize modules in existing package but failed")
666 };
667
668 let mut new_normalized = normalize_deserialized_modules(
669 pool,
670 upgrading_modules.into_iter(),
671 true,
672 );
673 for (name, cur_module) in current_normalized {
674 let Some(new_module) = new_normalized.remove(&name) else {
675 return Err(ExecutionError::new_with_source(
676 ExecutionErrorKind::PackageUpgradeError {
677 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
678 },
679 format!("Existing module {name} not found in next version of package"),
680 ));
681 };
682
683 check_module_compatibility(&policy, &cur_module, &new_module)?;
684 }
685
686 Ok(())
687 }
688
689 fn check_module_compatibility(
690 policy: &UpgradePolicy,
691 cur_module: &move_binary_format::compatibility::Module,
692 new_module: &move_binary_format::compatibility::Module,
693 ) -> Result<(), ExecutionError> {
694 match policy {
695 UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
696 UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
697 UpgradePolicy::Compatible => {
698 let compatibility = Compatibility::upgrade_check();
699
700 compatibility.check(cur_module, new_module)
701 }
702 }
703 .map_err(|e| {
704 ExecutionError::new_with_source(
705 ExecutionErrorKind::PackageUpgradeError {
706 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
707 },
708 e,
709 )
710 })
711 }
712
713 fn fetch_package(
714 context: &ExecutionContext<'_, '_, '_>,
715 package_id: &ObjectID,
716 ) -> Result<PackageObject, ExecutionError> {
717 let mut fetched_packages = fetch_packages(context, vec![package_id])?;
718 assert_invariant!(
719 fetched_packages.len() == 1,
720 "Number of fetched packages must match the number of package object IDs if successful."
721 );
722 match fetched_packages.pop() {
723 Some(pkg) => Ok(pkg),
724 None => invariant_violation!(
725 "We should always fetch a package for each object or return a dependency error."
726 ),
727 }
728 }
729
730 fn fetch_packages<'ctx, 'vm, 'state, 'a>(
731 context: &'ctx ExecutionContext<'vm, 'state, 'a>,
732 package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
733 ) -> Result<Vec<PackageObject>, ExecutionError> {
734 let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
735 match get_package_objects(&context.state_view, package_ids) {
736 Err(e) => Err(ExecutionError::new_with_source(
737 ExecutionErrorKind::PublishUpgradeMissingDependency,
738 e,
739 )),
740 Ok(Err(missing_deps)) => {
741 let msg = format!(
742 "Missing dependencies: {}",
743 missing_deps
744 .into_iter()
745 .map(|dep| format!("{}", dep))
746 .collect::<Vec<_>>()
747 .join(", ")
748 );
749 Err(ExecutionError::new_with_source(
750 ExecutionErrorKind::PublishUpgradeMissingDependency,
751 msg,
752 ))
753 }
754 Ok(Ok(pkgs)) => Ok(pkgs),
755 }
756 }
757
758 fn vm_move_call(
763 context: &mut ExecutionContext<'_, '_, '_>,
764 module_id: &ModuleId,
765 function: &IdentStr,
766 type_arguments: Vec<Type>,
767 tx_context_kind: TxContextKind,
768 mut serialized_arguments: Vec<Vec<u8>>,
769 ) -> Result<SerializedReturnValues, ExecutionError> {
770 match tx_context_kind {
771 TxContextKind::None => (),
772 TxContextKind::Mutable | TxContextKind::Immutable => {
773 serialized_arguments.push(context.tx_context.to_bcs_legacy_context());
774 }
775 }
776 let mut result = context
778 .execute_function_bypass_visibility(
779 module_id,
780 function,
781 type_arguments,
782 serialized_arguments,
783 )
784 .map_err(|e| context.convert_vm_error(e))?;
785
786 if tx_context_kind == TxContextKind::Mutable {
794 let Some((_, ctx_bytes, _)) = result.mutable_reference_outputs.pop() else {
795 invariant_violation!("Missing TxContext in reference outputs");
796 };
797 let updated_ctx: MoveLegacyTxContext = bcs::from_bytes(&ctx_bytes).map_err(|e| {
798 ExecutionError::invariant_violation(format!(
799 "Unable to deserialize TxContext bytes. {e}"
800 ))
801 })?;
802 context.tx_context.update_state(updated_ctx)?;
803 }
804 Ok(result)
805 }
806
807 #[allow(clippy::extra_unused_type_parameters)]
808 fn deserialize_modules<Mode: ExecutionMode>(
809 context: &mut ExecutionContext<'_, '_, '_>,
810 module_bytes: &[Vec<u8>],
811 ) -> Result<Vec<CompiledModule>, ExecutionError> {
812 let binary_config = context.protocol_config.binary_config(None);
813 let modules = module_bytes
814 .iter()
815 .map(|b| {
816 CompiledModule::deserialize_with_config(b, &binary_config)
817 .map_err(|e| e.finish(Location::Undefined))
818 })
819 .collect::<VMResult<Vec<CompiledModule>>>()
820 .map_err(|e| context.convert_vm_error(e))?;
821
822 assert_invariant!(
823 !modules.is_empty(),
824 "input checker ensures package is not empty"
825 );
826
827 Ok(modules)
828 }
829
830 fn publish_and_verify_modules(
831 context: &mut ExecutionContext<'_, '_, '_>,
832 package_id: ObjectID,
833 modules: &[CompiledModule],
834 ) -> Result<(), ExecutionError> {
835 let new_module_bytes: Vec<_> = modules
837 .iter()
838 .map(|m| {
839 let mut bytes = Vec::new();
840 m.serialize_with_version(VERSION_6, &mut bytes).unwrap();
841 bytes
842 })
843 .collect();
844 context
845 .publish_module_bundle(new_module_bytes, AccountAddress::from(package_id))
846 .map_err(|e| context.convert_vm_error(e))?;
847
848 for module in modules {
850 sui_verifier::verifier::sui_verify_module_unmetered(
853 module,
854 &BTreeMap::new(),
855 &context
856 .protocol_config
857 .verifier_config(None),
858 )?;
859 }
860
861 Ok(())
862 }
863
864 fn init_modules<Mode: ExecutionMode>(
865 context: &mut ExecutionContext<'_, '_, '_>,
866 argument_updates: &mut Mode::ArgumentUpdates,
867 modules: &[CompiledModule],
868 ) -> Result<(), ExecutionError> {
869 let modules_to_init = modules.iter().filter_map(|module| {
870 for fdef in &module.function_defs {
871 let fhandle = module.function_handle_at(fdef.function);
872 let fname = module.identifier_at(fhandle.name);
873 if fname == INIT_FN_NAME {
874 return Some(module.self_id());
875 }
876 }
877 None
878 });
879
880 for module_id in modules_to_init {
881 let return_values = execute_move_call::<Mode>(
882 context,
883 argument_updates,
884 &module_id,
885 INIT_FN_NAME,
886 vec![],
887 vec![],
888 true,
889 )?;
890
891 assert_invariant!(
892 return_values.is_empty(),
893 "init should not have return values"
894 )
895 }
896
897 Ok(())
898 }
899
900 #[derive(PartialEq, Eq, Clone, Copy)]
906 enum FunctionKind {
907 PrivateEntry,
908 PublicEntry,
909 NonEntry,
910 Init,
911 }
912
913 enum ValueKind {
915 Object {
916 type_: MoveObjectType,
917 has_public_transfer: bool,
918 },
919 Raw(Type, AbilitySet),
920 }
921
922 struct LoadedFunctionInfo {
923 kind: FunctionKind,
925 signature: LoadedFunctionInstantiation,
927 return_value_kinds: Vec<ValueKind>,
929 index: FunctionDefinitionIndex,
931 last_instr: CodeOffset,
933 }
934
935 fn check_visibility_and_signature<Mode: ExecutionMode>(
940 context: &mut ExecutionContext<'_, '_, '_>,
941 module_id: &ModuleId,
942 function: &IdentStr,
943 type_arguments: &[Type],
944 from_init: bool,
945 ) -> Result<LoadedFunctionInfo, ExecutionError> {
946 if from_init {
947 let result = context.load_function(module_id, function, type_arguments);
948 assert_invariant!(
949 result.is_ok(),
950 "The modules init should be able to be loaded"
951 );
952 }
953 let no_new_packages = vec![];
954 let data_store = SuiDataStore::new(&context.linkage_view, &no_new_packages);
955 let module = context
956 .vm
957 .get_runtime()
958 .load_module(module_id, &data_store)
959 .map_err(|e| context.convert_vm_error(e))?;
960 let Some((index, fdef)) = module
961 .function_defs
962 .iter()
963 .enumerate()
964 .find(|(_index, fdef)| {
965 module.identifier_at(module.function_handle_at(fdef.function).name) == function
966 })
967 else {
968 return Err(ExecutionError::new_with_source(
969 ExecutionErrorKind::FunctionNotFound,
970 format!(
971 "Could not resolve function '{}' in module {}",
972 function, &module_id,
973 ),
974 ));
975 };
976
977 if !from_init && function == INIT_FN_NAME && context.protocol_config.ban_entry_init() {
979 return Err(ExecutionError::new_with_source(
980 ExecutionErrorKind::NonEntryFunctionInvoked,
981 "Cannot call 'init'",
982 ));
983 }
984
985 let last_instr: CodeOffset = fdef
986 .code
987 .as_ref()
988 .map(|code| code.code.len() - 1)
989 .unwrap_or(0) as CodeOffset;
990 let function_kind = match (fdef.visibility, fdef.is_entry) {
991 (Visibility::Private | Visibility::Friend, true) => FunctionKind::PrivateEntry,
992 (Visibility::Public, true) => FunctionKind::PublicEntry,
993 (Visibility::Public, false) => FunctionKind::NonEntry,
994 (Visibility::Private, false) if from_init => {
995 assert_invariant!(
996 function == INIT_FN_NAME,
997 "module init specified non-init function"
998 );
999 FunctionKind::Init
1000 }
1001 (Visibility::Private | Visibility::Friend, false)
1002 if Mode::allow_arbitrary_function_calls() =>
1003 {
1004 FunctionKind::NonEntry
1005 }
1006 (Visibility::Private | Visibility::Friend, false) => {
1007 return Err(ExecutionError::new_with_source(
1008 ExecutionErrorKind::NonEntryFunctionInvoked,
1009 "Can only call `entry` or `public` functions",
1010 ));
1011 }
1012 };
1013 let signature = context
1014 .load_function(module_id, function, type_arguments)
1015 .map_err(|e| context.convert_vm_error(e))?;
1016 let signature =
1017 subst_signature(signature, type_arguments).map_err(|e| context.convert_vm_error(e))?;
1018 let return_value_kinds = match function_kind {
1019 FunctionKind::Init => {
1020 assert_invariant!(
1021 signature.return_.is_empty(),
1022 "init functions must have no return values"
1023 );
1024 vec![]
1025 }
1026 FunctionKind::PrivateEntry | FunctionKind::PublicEntry | FunctionKind::NonEntry => {
1027 check_non_entry_signature::<Mode>(context, module_id, function, &signature)?
1028 }
1029 };
1030 check_private_generics(context, module_id, function, type_arguments)?;
1031 Ok(LoadedFunctionInfo {
1032 kind: function_kind,
1033 signature,
1034 return_value_kinds,
1035 index: FunctionDefinitionIndex(index as u16),
1036 last_instr,
1037 })
1038 }
1039
1040 fn subst_signature(
1042 signature: LoadedFunctionInstantiation,
1043 type_arguments: &[Type],
1044 ) -> VMResult<LoadedFunctionInstantiation> {
1045 let LoadedFunctionInstantiation {
1046 parameters,
1047 return_,
1048 } = signature;
1049 let parameters = parameters
1050 .into_iter()
1051 .map(|ty| ty.subst(type_arguments))
1052 .collect::<PartialVMResult<Vec<_>>>()
1053 .map_err(|err| err.finish(Location::Undefined))?;
1054 let return_ = return_
1055 .into_iter()
1056 .map(|ty| ty.subst(type_arguments))
1057 .collect::<PartialVMResult<Vec<_>>>()
1058 .map_err(|err| err.finish(Location::Undefined))?;
1059 Ok(LoadedFunctionInstantiation {
1060 parameters,
1061 return_,
1062 })
1063 }
1064
1065 fn check_non_entry_signature<Mode: ExecutionMode>(
1068 context: &mut ExecutionContext<'_, '_, '_>,
1069 _module_id: &ModuleId,
1070 _function: &IdentStr,
1071 signature: &LoadedFunctionInstantiation,
1072 ) -> Result<Vec<ValueKind>, ExecutionError> {
1073 signature
1074 .return_
1075 .iter()
1076 .enumerate()
1077 .map(|(idx, return_type)| {
1078 let return_type = match return_type {
1079 Type::Reference(inner) | Type::MutableReference(inner)
1081 if Mode::allow_arbitrary_values() =>
1082 {
1083 inner
1084 }
1085 Type::Reference(_) | Type::MutableReference(_) => {
1086 return Err(ExecutionError::from_kind(
1087 ExecutionErrorKind::InvalidPublicFunctionReturnType { idx: idx as u16 },
1088 ))
1089 }
1090 t => t,
1091 };
1092 let abilities = context
1093 .vm
1094 .get_runtime()
1095 .get_type_abilities(return_type)
1096 .map_err(|e| context.convert_vm_error(e))?;
1097 Ok(match return_type {
1098 Type::MutableReference(_) | Type::Reference(_) => unreachable!(),
1099 Type::TyParam(_) => {
1100 invariant_violation!("TyParam should have been substituted")
1101 }
1102 Type::Datatype(_) | Type::DatatypeInstantiation(_) if abilities.has_key() => {
1103 let type_tag = context
1104 .vm
1105 .get_runtime()
1106 .get_type_tag(return_type)
1107 .map_err(|e| context.convert_vm_error(e))?;
1108 let TypeTag::Struct(struct_tag) = type_tag else {
1109 invariant_violation!("Struct type make a non struct type tag")
1110 };
1111 ValueKind::Object {
1112 type_: MoveObjectType::from(*struct_tag),
1113 has_public_transfer: abilities.has_store(),
1114 }
1115 }
1116 Type::Datatype(_)
1117 | Type::DatatypeInstantiation(_)
1118 | Type::Bool
1119 | Type::U8
1120 | Type::U64
1121 | Type::U128
1122 | Type::Address
1123 | Type::Signer
1124 | Type::Vector(_)
1125 | Type::U16
1126 | Type::U32
1127 | Type::U256 => ValueKind::Raw(return_type.clone(), abilities),
1128 })
1129 })
1130 .collect()
1131 }
1132
1133 fn check_private_generics(
1134 _context: &mut ExecutionContext,
1135 module_id: &ModuleId,
1136 function: &IdentStr,
1137 _type_arguments: &[Type],
1138 ) -> Result<(), ExecutionError> {
1139 let module_ident = (module_id.address(), module_id.name());
1140 if module_ident == (&SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
1141 return Err(ExecutionError::new_with_source(
1142 ExecutionErrorKind::NonEntryFunctionInvoked,
1143 format!("Cannot directly call functions in sui::{}", EVENT_MODULE),
1144 ));
1145 }
1146
1147 if module_ident == (&SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE)
1148 && PRIVATE_TRANSFER_FUNCTIONS.contains(&function)
1149 {
1150 let msg = format!(
1151 "Cannot directly call sui::{m}::{f}. \
1152 Use the public variant instead, sui::{m}::public_{f}",
1153 m = TRANSFER_MODULE,
1154 f = function
1155 );
1156 return Err(ExecutionError::new_with_source(
1157 ExecutionErrorKind::NonEntryFunctionInvoked,
1158 msg,
1159 ));
1160 }
1161
1162 Ok(())
1163 }
1164
1165 type ArgInfo = (
1166 TxContextKind,
1167 Vec<(LocalIndex, ValueKind)>,
1169 Vec<Vec<u8>>,
1170 );
1171
1172 fn build_move_args<Mode: ExecutionMode>(
1175 context: &mut ExecutionContext<'_, '_, '_>,
1176 module_id: &ModuleId,
1177 function: &IdentStr,
1178 function_kind: FunctionKind,
1179 signature: &LoadedFunctionInstantiation,
1180 args: &[Argument],
1181 ) -> Result<ArgInfo, ExecutionError> {
1182 let parameters = &signature.parameters;
1184 let tx_ctx_kind = match parameters.last() {
1185 Some(t) => is_tx_context(context, t)?,
1186 None => TxContextKind::None,
1187 };
1188 let has_one_time_witness = function_kind == FunctionKind::Init && parameters.len() == 2;
1192 let has_tx_context = tx_ctx_kind != TxContextKind::None;
1193 let num_args = args.len() + (has_one_time_witness as usize) + (has_tx_context as usize);
1194 if num_args != parameters.len() {
1195 return Err(ExecutionError::new_with_source(
1196 ExecutionErrorKind::ArityMismatch,
1197 format!(
1198 "Expected {:?} argument{} calling function '{}', but found {:?}",
1199 parameters.len(),
1200 if parameters.len() == 1 { "" } else { "s" },
1201 function,
1202 num_args
1203 ),
1204 ));
1205 }
1206
1207 let mut by_mut_ref = vec![];
1209 let mut serialized_args = Vec::with_capacity(num_args);
1210 let command_kind = CommandKind::MoveCall {
1211 package: (*module_id.address()).into(),
1212 module: module_id.name(),
1213 function,
1214 };
1215 if has_one_time_witness {
1219 let bcs_true_value = bcs::to_bytes(&true).unwrap();
1222 serialized_args.push(bcs_true_value)
1223 }
1224 for ((idx, arg), param_ty) in args.iter().copied().enumerate().zip(parameters) {
1225 let (value, non_ref_param_ty): (Value, &Type) = match param_ty {
1226 Type::MutableReference(inner) => {
1227 let value = context.borrow_arg_mut(idx, arg)?;
1228 let object_info = if let Value::Object(ObjectValue {
1229 type_,
1230 has_public_transfer,
1231 ..
1232 }) = &value
1233 {
1234 let type_tag = context
1235 .vm
1236 .get_runtime()
1237 .get_type_tag(type_)
1238 .map_err(|e| context.convert_vm_error(e))?;
1239 let TypeTag::Struct(struct_tag) = type_tag else {
1240 invariant_violation!("Struct type make a non struct type tag")
1241 };
1242 let type_ = (*struct_tag).into();
1243 ValueKind::Object {
1244 type_,
1245 has_public_transfer: *has_public_transfer,
1246 }
1247 } else {
1248 let abilities = context
1249 .vm
1250 .get_runtime()
1251 .get_type_abilities(inner)
1252 .map_err(|e| context.convert_vm_error(e))?;
1253 ValueKind::Raw((**inner).clone(), abilities)
1254 };
1255 by_mut_ref.push((idx as LocalIndex, object_info));
1256 (value, inner)
1257 }
1258 Type::Reference(inner) => (context.borrow_arg(idx, arg, param_ty)?, inner),
1259 t => {
1260 let value = context.by_value_arg(command_kind, idx, arg)?;
1261 (value, t)
1262 }
1263 };
1264 if matches!(
1265 function_kind,
1266 FunctionKind::PrivateEntry | FunctionKind::Init
1267 ) && value.was_used_in_non_entry_move_call()
1268 {
1269 return Err(command_argument_error(
1270 CommandArgumentError::InvalidArgumentToPrivateEntryFunction,
1271 idx,
1272 ));
1273 }
1274 check_param_type::<Mode>(context, idx, &value, non_ref_param_ty)?;
1275 let bytes = {
1276 let mut v = vec![];
1277 value.write_bcs_bytes(&mut v);
1278 v
1279 };
1280 serialized_args.push(bytes);
1281 }
1282 Ok((tx_ctx_kind, by_mut_ref, serialized_args))
1283 }
1284
1285 fn check_param_type<Mode: ExecutionMode>(
1287 context: &mut ExecutionContext<'_, '_, '_>,
1288 idx: usize,
1289 value: &Value,
1290 param_ty: &Type,
1291 ) -> Result<(), ExecutionError> {
1292 match value {
1293 Value::Raw(RawValueType::Any, _) if Mode::allow_arbitrary_values() => return Ok(()),
1296 Value::Raw(RawValueType::Any, bytes) => {
1300 let Some(layout) = primitive_serialization_layout(context, param_ty)? else {
1301 let msg = format!(
1302 "Non-primitive argument at index {}. If it is an object, it must be \
1303 populated by an object",
1304 idx,
1305 );
1306 return Err(ExecutionError::new_with_source(
1307 ExecutionErrorKind::command_argument_error(
1308 CommandArgumentError::InvalidUsageOfPureArg,
1309 idx as u16,
1310 ),
1311 msg,
1312 ));
1313 };
1314 bcs_argument_validate(bytes, idx as u16, layout)?;
1315 return Ok(());
1316 }
1317 Value::Raw(RawValueType::Loaded { ty, abilities, .. }, _) => {
1318 assert_invariant!(
1319 Mode::allow_arbitrary_values() || !abilities.has_key(),
1320 "Raw value should never be an object"
1321 );
1322 if ty != param_ty {
1323 return Err(command_argument_error(
1324 CommandArgumentError::TypeMismatch,
1325 idx,
1326 ));
1327 }
1328 }
1329 Value::Object(obj) => {
1330 let ty = &obj.type_;
1331 if ty != param_ty {
1332 return Err(command_argument_error(
1333 CommandArgumentError::TypeMismatch,
1334 idx,
1335 ));
1336 }
1337 }
1338 Value::Receiving(_, _, assigned_type) => {
1339 if let Some(assigned_type) = assigned_type {
1341 if assigned_type != param_ty {
1342 return Err(command_argument_error(
1343 CommandArgumentError::TypeMismatch,
1344 idx,
1345 ));
1346 }
1347 }
1348
1349 let Type::DatatypeInstantiation(struct_inst) = param_ty else {
1351 return Err(command_argument_error(
1352 CommandArgumentError::TypeMismatch,
1353 idx,
1354 ));
1355 };
1356 let (sidx, targs) = &**struct_inst;
1357 let Some(s) = context.vm.get_runtime().get_struct_type(*sidx) else {
1358 invariant_violation!("sui::transfer::Receiving struct not found in session")
1359 };
1360 let resolved_struct = get_datatype_ident(&s);
1361
1362 if resolved_struct != RESOLVED_RECEIVING_STRUCT || targs.len() != 1 {
1363 return Err(command_argument_error(
1364 CommandArgumentError::TypeMismatch,
1365 idx,
1366 ));
1367 }
1368 }
1369 }
1370 Ok(())
1371 }
1372
1373 fn get_datatype_ident(s: &CachedDatatype) -> (&AccountAddress, &IdentStr, &IdentStr) {
1374 let module_id = &s.defining_id;
1375 let struct_name = &s.name;
1376 (
1377 module_id.address(),
1378 module_id.name(),
1379 struct_name.as_ident_str(),
1380 )
1381 }
1382
1383 pub fn is_tx_context(
1387 context: &mut ExecutionContext<'_, '_, '_>,
1388 t: &Type,
1389 ) -> Result<TxContextKind, ExecutionError> {
1390 let (is_mut, inner) = match t {
1391 Type::MutableReference(inner) => (true, inner),
1392 Type::Reference(inner) => (false, inner),
1393 _ => return Ok(TxContextKind::None),
1394 };
1395 let Type::Datatype(idx) = &**inner else {
1396 return Ok(TxContextKind::None);
1397 };
1398 let Some(s) = context.vm.get_runtime().get_struct_type(*idx) else {
1399 invariant_violation!("Loaded struct not found")
1400 };
1401 let (module_addr, module_name, struct_name) = get_datatype_ident(&s);
1402 let is_tx_context_type = module_addr == &SUI_FRAMEWORK_ADDRESS
1403 && module_name == TX_CONTEXT_MODULE_NAME
1404 && struct_name == TX_CONTEXT_STRUCT_NAME;
1405 Ok(if is_tx_context_type {
1406 if is_mut {
1407 TxContextKind::Mutable
1408 } else {
1409 TxContextKind::Immutable
1410 }
1411 } else {
1412 TxContextKind::None
1413 })
1414 }
1415
1416 fn primitive_serialization_layout(
1418 context: &mut ExecutionContext<'_, '_, '_>,
1419 param_ty: &Type,
1420 ) -> Result<Option<PrimitiveArgumentLayout>, ExecutionError> {
1421 Ok(match param_ty {
1422 Type::Signer => return Ok(None),
1423 Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => {
1424 invariant_violation!("references and type parameters should be checked elsewhere")
1425 }
1426 Type::Bool => Some(PrimitiveArgumentLayout::Bool),
1427 Type::U8 => Some(PrimitiveArgumentLayout::U8),
1428 Type::U16 => Some(PrimitiveArgumentLayout::U16),
1429 Type::U32 => Some(PrimitiveArgumentLayout::U32),
1430 Type::U64 => Some(PrimitiveArgumentLayout::U64),
1431 Type::U128 => Some(PrimitiveArgumentLayout::U128),
1432 Type::U256 => Some(PrimitiveArgumentLayout::U256),
1433 Type::Address => Some(PrimitiveArgumentLayout::Address),
1434
1435 Type::Vector(inner) => {
1436 let info_opt = primitive_serialization_layout(context, inner)?;
1437 info_opt.map(|layout| PrimitiveArgumentLayout::Vector(Box::new(layout)))
1438 }
1439 Type::DatatypeInstantiation(struct_inst) => {
1440 let (idx, targs) = &**struct_inst;
1441 let Some(s) = context.vm.get_runtime().get_struct_type(*idx) else {
1442 invariant_violation!("Loaded struct not found")
1443 };
1444 let resolved_struct = get_datatype_ident(&s);
1445 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
1447 let info_opt = primitive_serialization_layout(context, &targs[0])?;
1448 info_opt.map(|layout| PrimitiveArgumentLayout::Option(Box::new(layout)))
1449 } else {
1450 None
1451 }
1452 }
1453 Type::Datatype(idx) => {
1454 let Some(s) = context.vm.get_runtime().get_struct_type(*idx) else {
1455 invariant_violation!("Loaded struct not found")
1456 };
1457 let resolved_struct = get_datatype_ident(&s);
1458 if resolved_struct == RESOLVED_SUI_ID {
1459 Some(PrimitiveArgumentLayout::Address)
1460 } else if resolved_struct == RESOLVED_ASCII_STR {
1461 Some(PrimitiveArgumentLayout::Ascii)
1462 } else if resolved_struct == RESOLVED_UTF8_STR {
1463 Some(PrimitiveArgumentLayout::UTF8)
1464 } else {
1465 None
1466 }
1467 }
1468 })
1469 }
1470
1471 #[derive(Debug)]
1479 pub enum PrimitiveArgumentLayout {
1480 Option(Box<PrimitiveArgumentLayout>),
1482 Vector(Box<PrimitiveArgumentLayout>),
1484 Ascii,
1486 UTF8,
1488 Bool,
1490 U8,
1491 U16,
1492 U32,
1493 U64,
1494 U128,
1495 U256,
1496 Address,
1497 }
1498
1499 impl PrimitiveArgumentLayout {
1500 pub fn bcs_only(&self) -> bool {
1504 match self {
1505 PrimitiveArgumentLayout::Option(_)
1507 | PrimitiveArgumentLayout::Ascii
1508 | PrimitiveArgumentLayout::UTF8 => false,
1509 PrimitiveArgumentLayout::Bool
1511 | PrimitiveArgumentLayout::U8
1512 | PrimitiveArgumentLayout::U16
1513 | PrimitiveArgumentLayout::U32
1514 | PrimitiveArgumentLayout::U64
1515 | PrimitiveArgumentLayout::U128
1516 | PrimitiveArgumentLayout::U256
1517 | PrimitiveArgumentLayout::Address => true,
1518 PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
1520 }
1521 }
1522 }
1523
1524 pub fn bcs_argument_validate(
1528 bytes: &[u8],
1529 idx: u16,
1530 layout: PrimitiveArgumentLayout,
1531 ) -> Result<(), ExecutionError> {
1532 bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
1533 ExecutionError::new_with_source(
1534 ExecutionErrorKind::command_argument_error(
1535 CommandArgumentError::InvalidBCSBytes,
1536 idx,
1537 ),
1538 format!("Function expects {layout} but provided argument's value does not match",),
1539 )
1540 })
1541 }
1542
1543 impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
1544 type Value = ();
1545 fn deserialize<D: serde::de::Deserializer<'d>>(
1546 self,
1547 deserializer: D,
1548 ) -> Result<Self::Value, D::Error> {
1549 use serde::de::Error;
1550 match self {
1551 PrimitiveArgumentLayout::Ascii => {
1552 let s: &str = serde::Deserialize::deserialize(deserializer)?;
1553 if !s.is_ascii() {
1554 Err(D::Error::custom("not an ascii string"))
1555 } else {
1556 Ok(())
1557 }
1558 }
1559 PrimitiveArgumentLayout::UTF8 => {
1560 deserializer.deserialize_string(serde::de::IgnoredAny)?;
1561 Ok(())
1562 }
1563 PrimitiveArgumentLayout::Option(layout) => {
1564 deserializer.deserialize_option(OptionElementVisitor(layout))
1565 }
1566 PrimitiveArgumentLayout::Vector(layout) => {
1567 deserializer.deserialize_seq(VectorElementVisitor(layout))
1568 }
1569 PrimitiveArgumentLayout::Bool => {
1572 deserializer.deserialize_bool(serde::de::IgnoredAny)?;
1573 Ok(())
1574 }
1575 PrimitiveArgumentLayout::U8 => {
1576 deserializer.deserialize_u8(serde::de::IgnoredAny)?;
1577 Ok(())
1578 }
1579 PrimitiveArgumentLayout::U16 => {
1580 deserializer.deserialize_u16(serde::de::IgnoredAny)?;
1581 Ok(())
1582 }
1583 PrimitiveArgumentLayout::U32 => {
1584 deserializer.deserialize_u32(serde::de::IgnoredAny)?;
1585 Ok(())
1586 }
1587 PrimitiveArgumentLayout::U64 => {
1588 deserializer.deserialize_u64(serde::de::IgnoredAny)?;
1589 Ok(())
1590 }
1591 PrimitiveArgumentLayout::U128 => {
1592 deserializer.deserialize_u128(serde::de::IgnoredAny)?;
1593 Ok(())
1594 }
1595 PrimitiveArgumentLayout::U256 => {
1596 U256::deserialize(deserializer)?;
1597 Ok(())
1598 }
1599 PrimitiveArgumentLayout::Address => {
1600 SuiAddress::deserialize(deserializer)?;
1601 Ok(())
1602 }
1603 }
1604 }
1605 }
1606
1607 struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
1608
1609 impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
1610 type Value = ();
1611
1612 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1613 formatter.write_str("Vector")
1614 }
1615
1616 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1617 where
1618 A: serde::de::SeqAccess<'d>,
1619 {
1620 while seq.next_element_seed(self.0)?.is_some() {}
1621 Ok(())
1622 }
1623 }
1624
1625 struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
1626
1627 impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
1628 type Value = ();
1629
1630 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1631 formatter.write_str("Option")
1632 }
1633
1634 fn visit_none<E>(self) -> Result<Self::Value, E>
1635 where
1636 E: serde::de::Error,
1637 {
1638 Ok(())
1639 }
1640
1641 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1642 where
1643 D: serde::Deserializer<'d>,
1644 {
1645 self.0.deserialize(deserializer)
1646 }
1647 }
1648
1649 impl fmt::Display for PrimitiveArgumentLayout {
1650 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1651 match self {
1652 PrimitiveArgumentLayout::Vector(inner) => {
1653 write!(f, "vector<{inner}>")
1654 }
1655 PrimitiveArgumentLayout::Option(inner) => {
1656 write!(f, "std::option::Option<{inner}>")
1657 }
1658 PrimitiveArgumentLayout::Ascii => {
1659 write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
1660 }
1661 PrimitiveArgumentLayout::UTF8 => {
1662 write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
1663 }
1664 PrimitiveArgumentLayout::Bool => write!(f, "bool"),
1665 PrimitiveArgumentLayout::U8 => write!(f, "u8"),
1666 PrimitiveArgumentLayout::U16 => write!(f, "u16"),
1667 PrimitiveArgumentLayout::U32 => write!(f, "u32"),
1668 PrimitiveArgumentLayout::U64 => write!(f, "u64"),
1669 PrimitiveArgumentLayout::U128 => write!(f, "u128"),
1670 PrimitiveArgumentLayout::U256 => write!(f, "u256"),
1671 PrimitiveArgumentLayout::Address => write!(f, "address"),
1672 }
1673 }
1674 }
1675}