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