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