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