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