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