1pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8 use crate::{
9 adapter::substitute_package_id,
10 data_store::{PackageStore, legacy::sui_data_store::SuiDataStore},
11 execution_mode::ExecutionMode,
12 execution_value::{
13 CommandKind, ExecutionState, ObjectContents, ObjectValue, RawValueType, Value,
14 ensure_serialized_size,
15 },
16 gas_charger::GasCharger,
17 programmable_transactions::{context::*, trace_utils},
18 static_programmable_transactions,
19 type_resolver::TypeTagResolver,
20 };
21 use move_binary_format::file_format::AbilitySet;
22 use move_binary_format::{
23 CompiledModule,
24 compatibility::{Compatibility, InclusionCheck},
25 errors::{Location, PartialVMResult, VMResult},
26 file_format::{CodeOffset, FunctionDefinitionIndex, LocalIndex, Visibility},
27 file_format_common::VERSION_6,
28 normalized,
29 };
30 use move_core_types::{
31 account_address::AccountAddress,
32 identifier::{IdentStr, Identifier},
33 language_storage::{ModuleId, StructTag, TypeTag},
34 u256::U256,
35 };
36 use move_trace_format::format::MoveTraceBuilder;
37 use move_vm_runtime::{
38 move_vm::MoveVM,
39 session::{LoadedFunctionInstantiation, SerializedReturnValues},
40 };
41 use move_vm_types::loaded_data::runtime_types::{CachedDatatype, Type};
42 use serde::{Deserialize, de::DeserializeSeed};
43 use std::{
44 cell::{OnceCell, RefCell},
45 collections::{BTreeMap, BTreeSet},
46 fmt,
47 rc::Rc,
48 sync::Arc,
49 time::Instant,
50 };
51 use sui_move_natives::object_runtime::ObjectRuntime;
52 use sui_protocol_config::ProtocolConfig;
53 use sui_types::{
54 SUI_FRAMEWORK_ADDRESS,
55 base_types::{
56 MoveLegacyTxContext, MoveObjectType, ObjectID, RESOLVED_ASCII_STR, RESOLVED_STD_OPTION,
57 RESOLVED_UTF8_STR, SuiAddress, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME,
58 TxContext, TxContextKind,
59 },
60 coin::Coin,
61 error::{ExecutionError, ExecutionErrorKind, command_argument_error},
62 execution::{ExecutionTiming, ResultWithTimings},
63 execution_status::{CommandArgumentError, PackageUpgradeError, TypeArgumentError},
64 id::RESOLVED_SUI_ID,
65 metrics::LimitsMetrics,
66 move_package::{
67 MovePackage, UpgradeCap, UpgradePolicy, UpgradeReceipt, UpgradeTicket,
68 normalize_deserialized_modules,
69 },
70 storage::{BackingPackageStore, PackageObject, get_package_objects},
71 transaction::{Command, ProgrammableMoveCall, ProgrammableTransaction},
72 transfer::RESOLVED_RECEIVING_STRUCT,
73 type_input::{StructInput, TypeInput},
74 };
75 use sui_verifier::{
76 INIT_FN_NAME,
77 private_generics::{EVENT_MODULE, PRIVATE_TRANSFER_FUNCTIONS, TRANSFER_MODULE},
78 private_generics_verifier_v2,
79 };
80 use tracing::instrument;
81
82 pub fn execute<Mode: ExecutionMode>(
83 protocol_config: &ProtocolConfig,
84 metrics: Arc<LimitsMetrics>,
85 vm: &MoveVM,
86 state_view: &mut dyn ExecutionState,
87 package_store: &dyn BackingPackageStore,
88 tx_context: Rc<RefCell<TxContext>>,
89 gas_charger: &mut GasCharger,
90 pt: ProgrammableTransaction,
91 trace_builder_opt: &mut Option<MoveTraceBuilder>,
92 ) -> ResultWithTimings<Mode::ExecutionResults, ExecutionError> {
93 if protocol_config.enable_ptb_execution_v2() {
94 return static_programmable_transactions::execute::<Mode>(
95 protocol_config,
96 metrics,
97 vm,
98 state_view,
99 package_store,
100 tx_context,
101 gas_charger,
102 pt,
103 trace_builder_opt,
104 );
105 }
106
107 let mut timings = vec![];
108 let result = execute_inner::<Mode>(
109 &mut timings,
110 protocol_config,
111 metrics,
112 vm,
113 state_view,
114 tx_context,
115 gas_charger,
116 pt,
117 trace_builder_opt,
118 );
119
120 match result {
121 Ok(result) => Ok((result, timings)),
122 Err(e) => {
123 trace_utils::trace_execution_error(trace_builder_opt, e.to_string());
124
125 Err((e, timings))
126 }
127 }
128 }
129
130 pub fn execute_inner<Mode: ExecutionMode>(
131 timings: &mut Vec<ExecutionTiming>,
132 protocol_config: &ProtocolConfig,
133 metrics: Arc<LimitsMetrics>,
134 vm: &MoveVM,
135 state_view: &mut dyn ExecutionState,
136 tx_context: Rc<RefCell<TxContext>>,
137 gas_charger: &mut GasCharger,
138 pt: ProgrammableTransaction,
139 trace_builder_opt: &mut Option<MoveTraceBuilder>,
140 ) -> Result<Mode::ExecutionResults, ExecutionError> {
141 let ProgrammableTransaction { inputs, commands } = pt;
142 let mut context = ExecutionContext::new(
143 protocol_config,
144 metrics,
145 vm,
146 state_view,
147 tx_context,
148 gas_charger,
149 inputs,
150 )?;
151
152 trace_utils::trace_ptb_summary::<Mode>(&mut context, trace_builder_opt, &commands)?;
153
154 let mut mode_results = Mode::empty_results();
156 for (idx, command) in commands.into_iter().enumerate() {
157 let start = Instant::now();
158 if let Err(err) =
159 execute_command::<Mode>(&mut context, &mut mode_results, command, trace_builder_opt)
160 {
161 let object_runtime: &ObjectRuntime = context.object_runtime()?;
162 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
164 drop(context);
166 state_view.save_loaded_runtime_objects(loaded_runtime_objects);
167 timings.push(ExecutionTiming::Abort(start.elapsed()));
168 return Err(err.with_command_index(idx));
169 };
170 timings.push(ExecutionTiming::Success(start.elapsed()));
171 }
172
173 let object_runtime: &ObjectRuntime = context.object_runtime()?;
175 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
179 let wrapped_object_containers = object_runtime.wrapped_object_containers();
182 let generated_object_ids = object_runtime.generated_object_ids();
184
185 let finished = context.finish::<Mode>();
187 state_view.save_loaded_runtime_objects(loaded_runtime_objects);
189 state_view.save_wrapped_object_containers(wrapped_object_containers);
190 state_view.record_execution_results(finished?)?;
191 state_view.record_generated_object_ids(generated_object_ids);
192 Ok(mode_results)
193 }
194
195 #[instrument(level = "trace", skip_all)]
197 fn execute_command<Mode: ExecutionMode>(
198 context: &mut ExecutionContext<'_, '_, '_>,
199 mode_results: &mut Mode::ExecutionResults,
200 command: Command,
201 trace_builder_opt: &mut Option<MoveTraceBuilder>,
202 ) -> Result<(), ExecutionError> {
203 let mut argument_updates = Mode::empty_arguments();
204
205 let kind = match &command {
206 Command::MakeMoveVec(_, _) => CommandKind::MakeMoveVec,
207 Command::TransferObjects(_, _) => CommandKind::TransferObjects,
208 Command::SplitCoins(_, _) => CommandKind::SplitCoins,
209 Command::MergeCoins(_, _) => CommandKind::MergeCoins,
210 Command::MoveCall(_) => CommandKind::MoveCall,
211 Command::Publish(_, _) => CommandKind::Publish,
212 Command::Upgrade(_, _, _, _) => CommandKind::Upgrade,
213 };
214 let results = match command {
215 Command::MakeMoveVec(tag_opt, args) if args.is_empty() => {
216 let Some(tag) = tag_opt else {
217 invariant_violation!(
218 "input checker ensures if args are empty, there is a type specified"
219 );
220 };
221
222 let tag = to_type_tag(context, tag, 0)?;
223
224 let elem_ty = context.load_type(&tag).map_err(|e| {
225 if context.protocol_config.convert_type_argument_error() {
226 context.convert_type_argument_error(0, e)
227 } else {
228 context.convert_vm_error(e)
229 }
230 })?;
231
232 let ty = Type::Vector(Box::new(elem_ty));
233 let abilities = context.get_type_abilities(&ty)?;
234 let bytes = bcs::to_bytes::<Vec<u8>>(&vec![]).unwrap();
236
237 trace_utils::trace_make_move_vec(context, trace_builder_opt, vec![], &ty)?;
238
239 vec![Value::Raw(
240 RawValueType::Loaded {
241 ty,
242 abilities,
243 used_in_non_entry_move_call: false,
244 },
245 bytes,
246 )]
247 }
248 Command::MakeMoveVec(tag_opt, args) => {
249 let args = context.splat_args(0, args)?;
250 let elem_abilities = OnceCell::<AbilitySet>::new();
251 let mut res = vec![];
252 leb128::write::unsigned(&mut res, args.len() as u64).unwrap();
253 let mut arg_iter = args.into_iter().enumerate();
254 let mut move_values = vec![];
255 let (mut used_in_non_entry_move_call, elem_ty) = match tag_opt {
256 Some(tag) => {
257 let tag = to_type_tag(context, tag, 0)?;
258 let elem_ty = context.load_type(&tag).map_err(|e| {
259 if context.protocol_config.convert_type_argument_error() {
260 context.convert_type_argument_error(0, e)
261 } else {
262 context.convert_vm_error(e)
263 }
264 })?;
265 (false, elem_ty)
266 }
267 None => {
269 let (idx, arg) = arg_iter.next().unwrap();
271 let obj: ObjectValue =
272 context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
273 trace_utils::add_move_value_info_from_obj_value(
274 context,
275 trace_builder_opt,
276 &mut move_values,
277 &obj,
278 )?;
279 let bound =
280 amplification_bound::<Mode>(context, &obj.type_, &elem_abilities)?;
281 obj.write_bcs_bytes(
282 &mut res,
283 bound.map(|b| context.size_bound_vector_elem(b)),
284 )?;
285 (obj.used_in_non_entry_move_call, obj.type_)
286 }
287 };
288 for (idx, arg) in arg_iter {
289 let value: Value = context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
290 trace_utils::add_move_value_info_from_value(
291 context,
292 trace_builder_opt,
293 &mut move_values,
294 &elem_ty,
295 &value,
296 )?;
297 check_param_type::<Mode>(context, idx, &value, &elem_ty)?;
298 used_in_non_entry_move_call =
299 used_in_non_entry_move_call || value.was_used_in_non_entry_move_call();
300 let bound = amplification_bound::<Mode>(context, &elem_ty, &elem_abilities)?;
301 value.write_bcs_bytes(
302 &mut res,
303 bound.map(|b| context.size_bound_vector_elem(b)),
304 )?;
305 }
306 let ty = Type::Vector(Box::new(elem_ty));
307 let abilities = context.get_type_abilities(&ty)?;
308
309 trace_utils::trace_make_move_vec(context, trace_builder_opt, move_values, &ty)?;
310
311 vec![Value::Raw(
312 RawValueType::Loaded {
313 ty,
314 abilities,
315 used_in_non_entry_move_call,
316 },
317 res,
318 )]
319 }
320 Command::TransferObjects(objs, addr_arg) => {
321 let unsplat_objs_len = objs.len();
322 let objs = context.splat_args(0, objs)?;
323 let addr_arg = context.one_arg(unsplat_objs_len, addr_arg)?;
324 let objs: Vec<ObjectValue> = objs
325 .into_iter()
326 .enumerate()
327 .map(|(idx, arg)| context.by_value_arg(CommandKind::TransferObjects, idx, arg))
328 .collect::<Result<_, _>>()?;
329 let addr: SuiAddress =
330 context.by_value_arg(CommandKind::TransferObjects, objs.len(), addr_arg)?;
331
332 trace_utils::trace_transfer(context, trace_builder_opt, &objs)?;
333
334 for obj in objs {
335 obj.ensure_public_transfer_eligible()?;
336 context.transfer_object(obj, addr)?;
337 }
338 vec![]
339 }
340 Command::SplitCoins(coin_arg, amount_args) => {
341 let coin_arg = context.one_arg(0, coin_arg)?;
342 let amount_args = context.splat_args(1, amount_args)?;
343 let mut obj: ObjectValue = context.borrow_arg_mut(0, coin_arg)?;
344 let ObjectContents::Coin(coin) = &mut obj.contents else {
345 let e = ExecutionErrorKind::command_argument_error(
346 CommandArgumentError::TypeMismatch,
347 0,
348 );
349 let msg = "Expected a coin but got an non coin object".to_owned();
350 return Err(ExecutionError::new_with_source(e, msg));
351 };
352 let split_coins: Vec<Value> = amount_args
353 .into_iter()
354 .map(|amount_arg| {
355 let amount: u64 =
356 context.by_value_arg(CommandKind::SplitCoins, 1, amount_arg)?;
357 let new_coin_id = context.fresh_id()?;
358 let new_coin = coin.split(amount, new_coin_id)?;
359 let coin_type = obj.type_.clone();
360 let new_coin = unsafe { ObjectValue::coin(coin_type, new_coin) };
363 Ok(Value::Object(new_coin))
364 })
365 .collect::<Result<_, ExecutionError>>()?;
366
367 trace_utils::trace_split_coins(
368 context,
369 trace_builder_opt,
370 &obj.type_,
371 coin,
372 &split_coins,
373 )?;
374
375 context.restore_arg::<Mode>(&mut argument_updates, coin_arg, Value::Object(obj))?;
376 split_coins
377 }
378 Command::MergeCoins(target_arg, coin_args) => {
379 let target_arg = context.one_arg(0, target_arg)?;
380 let coin_args = context.splat_args(1, coin_args)?;
381 let mut target: ObjectValue = context.borrow_arg_mut(0, target_arg)?;
382 let ObjectContents::Coin(target_coin) = &mut target.contents else {
383 let e = ExecutionErrorKind::command_argument_error(
384 CommandArgumentError::TypeMismatch,
385 0,
386 );
387 let msg = "Expected a coin but got an non coin object".to_owned();
388 return Err(ExecutionError::new_with_source(e, msg));
389 };
390 let coins: Vec<ObjectValue> = coin_args
391 .into_iter()
392 .enumerate()
393 .map(|(idx, arg)| context.by_value_arg(CommandKind::MergeCoins, idx + 1, arg))
394 .collect::<Result<_, _>>()?;
395 let mut input_infos = vec![];
396 for (idx, coin) in coins.into_iter().enumerate() {
397 if target.type_ != coin.type_ {
398 let e = ExecutionErrorKind::command_argument_error(
399 CommandArgumentError::TypeMismatch,
400 (idx + 1) as u16,
401 );
402 let msg = "Coins do not have the same type".to_owned();
403 return Err(ExecutionError::new_with_source(e, msg));
404 }
405 let ObjectContents::Coin(Coin { id, balance }) = coin.contents else {
406 invariant_violation!(
407 "Target coin was a coin, and we already checked for the same type. \
408 This should be a coin"
409 );
410 };
411 trace_utils::add_coin_obj_info(
412 trace_builder_opt,
413 &mut input_infos,
414 balance.value(),
415 *id.object_id(),
416 );
417 context.delete_id(*id.object_id())?;
418 target_coin.add(balance)?;
419 }
420
421 trace_utils::trace_merge_coins(
422 context,
423 trace_builder_opt,
424 &target.type_,
425 &input_infos,
426 target_coin,
427 )?;
428
429 context.restore_arg::<Mode>(
430 &mut argument_updates,
431 target_arg,
432 Value::Object(target),
433 )?;
434 vec![]
435 }
436 Command::MoveCall(move_call) => {
437 let ProgrammableMoveCall {
438 package,
439 module,
440 function,
441 type_arguments,
442 arguments,
443 } = *move_call;
444 trace_utils::trace_move_call_start(trace_builder_opt);
445
446 let arguments = context.splat_args(0, arguments)?;
447
448 let module = to_identifier(context, module)?;
449 let function = to_identifier(context, function)?;
450
451 let mut loaded_type_arguments = Vec::with_capacity(type_arguments.len());
453 for (ix, type_arg) in type_arguments.into_iter().enumerate() {
454 let type_arg = to_type_tag(context, type_arg, ix)?;
455 let ty = context
456 .load_type(&type_arg)
457 .map_err(|e| context.convert_type_argument_error(ix, e))?;
458 loaded_type_arguments.push(ty);
459 }
460
461 let original_address = context.set_link_context(package)?;
462 let storage_id = ModuleId::new(*package, module.clone());
463 let runtime_id = ModuleId::new(original_address, module);
464 let return_values = execute_move_call::<Mode>(
465 context,
466 &mut argument_updates,
467 &storage_id,
468 &runtime_id,
469 &function,
470 loaded_type_arguments,
471 arguments,
472 false,
473 trace_builder_opt,
474 );
475
476 trace_utils::trace_move_call_end(trace_builder_opt);
477
478 context.linkage_view.reset_linkage()?;
479 return_values?
480 }
481 Command::Publish(modules, dep_ids) => {
482 trace_utils::trace_publish_event(trace_builder_opt)?;
483
484 execute_move_publish::<Mode>(
485 context,
486 &mut argument_updates,
487 modules,
488 dep_ids,
489 trace_builder_opt,
490 )?
491 }
492 Command::Upgrade(modules, dep_ids, current_package_id, upgrade_ticket) => {
493 trace_utils::trace_upgrade_event(trace_builder_opt)?;
494
495 let upgrade_ticket = context.one_arg(0, upgrade_ticket)?;
496 execute_move_upgrade::<Mode>(
497 context,
498 modules,
499 dep_ids,
500 current_package_id,
501 upgrade_ticket,
502 )?
503 }
504 };
505
506 Mode::finish_command(context, mode_results, argument_updates, &results)?;
507 context.push_command_results(kind, results)?;
508 Ok(())
509 }
510
511 fn execute_move_call<Mode: ExecutionMode>(
513 context: &mut ExecutionContext<'_, '_, '_>,
514 argument_updates: &mut Mode::ArgumentUpdates,
515 storage_id: &ModuleId,
516 runtime_id: &ModuleId,
517 function: &IdentStr,
518 type_arguments: Vec<Type>,
519 arguments: Vec<Arg>,
520 is_init: bool,
521 trace_builder_opt: &mut Option<MoveTraceBuilder>,
522 ) -> Result<Vec<Value>, ExecutionError> {
523 let LoadedFunctionInfo {
525 kind,
526 signature,
527 return_value_kinds,
528 index,
529 last_instr,
530 } = check_visibility_and_signature::<Mode>(
531 context,
532 runtime_id,
533 function,
534 &type_arguments,
535 is_init,
536 )?;
537 let (tx_context_kind, by_mut_ref, serialized_arguments) =
539 build_move_args::<Mode>(context, runtime_id, function, kind, &signature, &arguments)?;
540 let SerializedReturnValues {
542 mutable_reference_outputs,
543 return_values,
544 } = vm_move_call(
545 context,
546 runtime_id,
547 function,
548 type_arguments,
549 tx_context_kind,
550 serialized_arguments,
551 trace_builder_opt,
552 )?;
553 assert_invariant!(
554 by_mut_ref.len() == mutable_reference_outputs.len(),
555 "lost mutable input"
556 );
557
558 if context.protocol_config.relocate_event_module() {
559 context.take_user_events(storage_id, index, last_instr)?;
560 } else {
561 context.take_user_events(runtime_id, index, last_instr)?;
562 }
563
564 let saved_linkage = context.linkage_view.steal_linkage();
567 let used_in_non_entry_move_call = kind == FunctionKind::NonEntry;
570 let res = write_back_results::<Mode>(
571 context,
572 argument_updates,
573 &arguments,
574 used_in_non_entry_move_call,
575 mutable_reference_outputs
576 .into_iter()
577 .map(|(i, bytes, _layout)| (i, bytes)),
578 by_mut_ref,
579 return_values.into_iter().map(|(bytes, _layout)| bytes),
580 return_value_kinds,
581 );
582
583 context.linkage_view.restore_linkage(saved_linkage)?;
584 res
585 }
586
587 fn write_back_results<Mode: ExecutionMode>(
588 context: &mut ExecutionContext<'_, '_, '_>,
589 argument_updates: &mut Mode::ArgumentUpdates,
590 arguments: &[Arg],
591 non_entry_move_call: bool,
592 mut_ref_values: impl IntoIterator<Item = (u8, Vec<u8>)>,
593 mut_ref_kinds: impl IntoIterator<Item = (u8, ValueKind)>,
594 return_values: impl IntoIterator<Item = Vec<u8>>,
595 return_value_kinds: impl IntoIterator<Item = ValueKind>,
596 ) -> Result<Vec<Value>, ExecutionError> {
597 for ((i, bytes), (j, kind)) in mut_ref_values.into_iter().zip(mut_ref_kinds) {
598 assert_invariant!(i == j, "lost mutable input");
599 let arg_idx = i as usize;
600 let value = make_value(context, kind, bytes, non_entry_move_call)?;
601 context.restore_arg::<Mode>(argument_updates, arguments[arg_idx], value)?;
602 }
603
604 return_values
605 .into_iter()
606 .zip(return_value_kinds)
607 .map(|(bytes, kind)| {
608 make_value(
610 context, kind, bytes, true,
611 )
612 })
613 .collect()
614 }
615
616 fn make_value(
617 context: &mut ExecutionContext<'_, '_, '_>,
618 value_info: ValueKind,
619 bytes: Vec<u8>,
620 used_in_non_entry_move_call: bool,
621 ) -> Result<Value, ExecutionError> {
622 Ok(match value_info {
623 ValueKind::Object {
624 type_,
625 has_public_transfer,
626 } => Value::Object(context.make_object_value(
627 type_,
628 has_public_transfer,
629 used_in_non_entry_move_call,
630 &bytes,
631 )?),
632 ValueKind::Raw(ty, abilities) => Value::Raw(
633 RawValueType::Loaded {
634 ty,
635 abilities,
636 used_in_non_entry_move_call,
637 },
638 bytes,
639 ),
640 })
641 }
642
643 fn execute_move_publish<Mode: ExecutionMode>(
646 context: &mut ExecutionContext<'_, '_, '_>,
647 argument_updates: &mut Mode::ArgumentUpdates,
648 module_bytes: Vec<Vec<u8>>,
649 dep_ids: Vec<ObjectID>,
650 trace_builder_opt: &mut Option<MoveTraceBuilder>,
651 ) -> Result<Vec<Value>, ExecutionError> {
652 assert_invariant!(
653 !module_bytes.is_empty(),
654 "empty package is checked in transaction input checker"
655 );
656 context
657 .gas_charger
658 .charge_publish_package(module_bytes.iter().map(|v| v.len()).sum())?;
659
660 let mut modules = context.deserialize_modules(&module_bytes)?;
661
662 let runtime_id = if Mode::packages_are_predefined() {
666 (*modules[0].self_id().address()).into()
668 } else {
669 let id = context.tx_context.borrow_mut().fresh_id();
670 substitute_package_id(&mut modules, id)?;
671 id
672 };
673
674 let storage_id = runtime_id;
676 let dependencies = fetch_packages(&context.state_view, &dep_ids)?;
677 let package =
678 context.new_package(&modules, dependencies.iter().map(|p| p.move_package()))?;
679
680 context.linkage_view.set_linkage(&package)?;
686 context.write_package(package);
687 let res = publish_and_verify_modules(context, runtime_id, &modules).and_then(|_| {
688 init_modules::<Mode>(context, argument_updates, &modules, trace_builder_opt)
689 });
690 context.linkage_view.reset_linkage()?;
691 if res.is_err() {
692 context.pop_package();
693 }
694 res?;
695
696 let values = if Mode::packages_are_predefined() {
697 vec![]
699 } else {
700 let cap = &UpgradeCap::new(context.fresh_id()?, storage_id);
701 vec![Value::Object(context.make_object_value(
702 UpgradeCap::type_().into(),
703 true,
704 false,
705 &bcs::to_bytes(cap).unwrap(),
706 )?)]
707 };
708 Ok(values)
709 }
710
711 fn execute_move_upgrade<Mode: ExecutionMode>(
713 context: &mut ExecutionContext<'_, '_, '_>,
714 module_bytes: Vec<Vec<u8>>,
715 dep_ids: Vec<ObjectID>,
716 current_package_id: ObjectID,
717 upgrade_ticket_arg: Arg,
718 ) -> Result<Vec<Value>, ExecutionError> {
719 assert_invariant!(
720 !module_bytes.is_empty(),
721 "empty package is checked in transaction input checker"
722 );
723 context
724 .gas_charger
725 .charge_upgrade_package(module_bytes.iter().map(|v| v.len()).sum())?;
726
727 let upgrade_ticket_type = context
728 .load_type_from_struct(&UpgradeTicket::type_())
729 .map_err(|e| context.convert_vm_error(e))?;
730 let upgrade_receipt_type = context
731 .load_type_from_struct(&UpgradeReceipt::type_())
732 .map_err(|e| context.convert_vm_error(e))?;
733
734 let upgrade_ticket: UpgradeTicket = {
735 let mut ticket_bytes = Vec::new();
736 let ticket_val: Value =
737 context.by_value_arg(CommandKind::Upgrade, 0, upgrade_ticket_arg)?;
738 check_param_type::<Mode>(context, 0, &ticket_val, &upgrade_ticket_type)?;
739 let bound =
740 amplification_bound::<Mode>(context, &upgrade_ticket_type, &OnceCell::new())?;
741 ticket_val
742 .write_bcs_bytes(&mut ticket_bytes, bound.map(|b| context.size_bound_raw(b)))?;
743 bcs::from_bytes(&ticket_bytes).map_err(|_| {
744 ExecutionError::from_kind(ExecutionErrorKind::CommandArgumentError {
745 arg_idx: 0,
746 kind: CommandArgumentError::InvalidBCSBytes,
747 })
748 })?
749 };
750
751 if current_package_id != upgrade_ticket.package.bytes {
753 return Err(ExecutionError::from_kind(
754 ExecutionErrorKind::PackageUpgradeError {
755 upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
756 package_id: current_package_id,
757 ticket_id: upgrade_ticket.package.bytes,
758 },
759 },
760 ));
761 }
762
763 let hash_modules = true;
765 let computed_digest =
766 MovePackage::compute_digest_for_modules_and_deps(&module_bytes, &dep_ids, hash_modules)
767 .to_vec();
768 if computed_digest != upgrade_ticket.digest {
769 return Err(ExecutionError::from_kind(
770 ExecutionErrorKind::PackageUpgradeError {
771 upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
772 digest: computed_digest,
773 },
774 },
775 ));
776 }
777
778 let current_package = fetch_package(&context.state_view, &upgrade_ticket.package.bytes)?;
780
781 let mut modules = context.deserialize_modules(&module_bytes)?;
782 let runtime_id = current_package.move_package().original_package_id();
783 substitute_package_id(&mut modules, runtime_id)?;
784
785 let storage_id = context.tx_context.borrow_mut().fresh_id();
787
788 let dependencies = fetch_packages(&context.state_view, &dep_ids)?;
789 let package = context.upgrade_package(
790 storage_id,
791 current_package.move_package(),
792 &modules,
793 dependencies.iter().map(|p| p.move_package()),
794 )?;
795
796 context.linkage_view.set_linkage(&package)?;
797 let res = publish_and_verify_modules(context, runtime_id, &modules);
798 context.linkage_view.reset_linkage()?;
799 res?;
800
801 check_compatibility(
802 context.protocol_config,
803 current_package.move_package(),
804 &modules,
805 upgrade_ticket.policy,
806 )?;
807 if context.protocol_config.check_for_init_during_upgrade() {
808 let current_module_names: BTreeSet<&str> = current_package
811 .move_package()
812 .serialized_module_map()
813 .keys()
814 .map(|s| s.as_str())
815 .collect();
816 let upgrade_module_names: BTreeSet<&str> = package
817 .serialized_module_map()
818 .keys()
819 .map(|s| s.as_str())
820 .collect();
821 let new_module_names = upgrade_module_names
822 .difference(¤t_module_names)
823 .copied()
824 .collect::<BTreeSet<&str>>();
825 let new_modules = modules
826 .iter()
827 .filter(|m| {
828 let name = m.identifier_at(m.self_handle().name).as_str();
829 new_module_names.contains(name)
830 })
831 .collect::<Vec<&CompiledModule>>();
832 let new_module_has_init = new_modules.iter().any(|module| {
833 module.function_defs.iter().any(|fdef| {
834 let fhandle = module.function_handle_at(fdef.function);
835 let fname = module.identifier_at(fhandle.name);
836 fname == INIT_FN_NAME
837 })
838 });
839 if new_module_has_init {
840 return Err(ExecutionError::new_with_source(
842 ExecutionErrorKind::FeatureNotYetSupported,
843 "`init` in new modules on upgrade is not yet supported",
844 ));
845 }
846 }
847
848 context.write_package(package);
849 Ok(vec![Value::Raw(
850 RawValueType::Loaded {
851 ty: upgrade_receipt_type,
852 abilities: AbilitySet::EMPTY,
853 used_in_non_entry_move_call: false,
854 },
855 bcs::to_bytes(&UpgradeReceipt::new(upgrade_ticket, storage_id)).unwrap(),
856 )])
857 }
858
859 pub fn check_compatibility(
860 protocol_config: &ProtocolConfig,
861 existing_package: &MovePackage,
862 upgrading_modules: &[CompiledModule],
863 policy: u8,
864 ) -> Result<(), ExecutionError> {
865 let Ok(policy) = UpgradePolicy::try_from(policy) else {
867 return Err(ExecutionError::from_kind(
868 ExecutionErrorKind::PackageUpgradeError {
869 upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
870 },
871 ));
872 };
873
874 let pool = &mut normalized::RcPool::new();
875 let binary_config = protocol_config.binary_config(None);
876 let Ok(current_normalized) =
877 existing_package.normalize(pool, &binary_config, true)
878 else {
879 invariant_violation!("Tried to normalize modules in existing package but failed")
880 };
881
882 let existing_modules_len = current_normalized.len();
883 let upgrading_modules_len = upgrading_modules.len();
884 let disallow_new_modules = protocol_config.disallow_new_modules_in_deps_only_packages()
885 && policy as u8 == UpgradePolicy::DEP_ONLY;
886
887 if disallow_new_modules && existing_modules_len != upgrading_modules_len {
888 return Err(ExecutionError::new_with_source(
889 ExecutionErrorKind::PackageUpgradeError {
890 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
891 },
892 format!(
893 "Existing package has {existing_modules_len} modules, but new package has \
894 {upgrading_modules_len}. Adding or removing a module to a deps only package is not allowed."
895 ),
896 ));
897 }
898
899 let mut new_normalized = normalize_deserialized_modules(
900 pool,
901 upgrading_modules.iter(),
902 true,
903 );
904 for (name, cur_module) in current_normalized {
905 let Some(new_module) = new_normalized.remove(&name) else {
906 return Err(ExecutionError::new_with_source(
907 ExecutionErrorKind::PackageUpgradeError {
908 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
909 },
910 format!("Existing module {name} not found in next version of package"),
911 ));
912 };
913
914 check_module_compatibility(&policy, &cur_module, &new_module)?;
915 }
916
917 debug_assert!(!disallow_new_modules || new_normalized.is_empty());
919
920 Ok(())
921 }
922
923 fn check_module_compatibility(
924 policy: &UpgradePolicy,
925 cur_module: &move_binary_format::compatibility::Module,
926 new_module: &move_binary_format::compatibility::Module,
927 ) -> Result<(), ExecutionError> {
928 match policy {
929 UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
930 UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
931 UpgradePolicy::Compatible => {
932 let compatibility = Compatibility::upgrade_check();
933
934 compatibility.check(cur_module, new_module)
935 }
936 }
937 .map_err(|e| {
938 ExecutionError::new_with_source(
939 ExecutionErrorKind::PackageUpgradeError {
940 upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
941 },
942 e,
943 )
944 })
945 }
946
947 pub fn fetch_package(
948 state_view: &impl BackingPackageStore,
949 package_id: &ObjectID,
950 ) -> Result<PackageObject, ExecutionError> {
951 let mut fetched_packages = fetch_packages(state_view, vec![package_id])?;
952 assert_invariant!(
953 fetched_packages.len() == 1,
954 "Number of fetched packages must match the number of package object IDs if successful."
955 );
956 match fetched_packages.pop() {
957 Some(pkg) => Ok(pkg),
958 None => invariant_violation!(
959 "We should always fetch a package for each object or return a dependency error."
960 ),
961 }
962 }
963
964 pub fn fetch_packages<'ctx, 'state>(
965 state_view: &'state impl BackingPackageStore,
966 package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
967 ) -> Result<Vec<PackageObject>, ExecutionError> {
968 let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
969 match get_package_objects(state_view, package_ids) {
970 Err(e) => Err(ExecutionError::new_with_source(
971 ExecutionErrorKind::PublishUpgradeMissingDependency,
972 e,
973 )),
974 Ok(Err(missing_deps)) => {
975 let msg = format!(
976 "Missing dependencies: {}",
977 missing_deps
978 .into_iter()
979 .map(|dep| format!("{}", dep))
980 .collect::<Vec<_>>()
981 .join(", ")
982 );
983 Err(ExecutionError::new_with_source(
984 ExecutionErrorKind::PublishUpgradeMissingDependency,
985 msg,
986 ))
987 }
988 Ok(Ok(pkgs)) => Ok(pkgs),
989 }
990 }
991
992 fn vm_move_call(
997 context: &mut ExecutionContext<'_, '_, '_>,
998 module_id: &ModuleId,
999 function: &IdentStr,
1000 type_arguments: Vec<Type>,
1001 tx_context_kind: TxContextKind,
1002 mut serialized_arguments: Vec<Vec<u8>>,
1003 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1004 ) -> Result<SerializedReturnValues, ExecutionError> {
1005 match tx_context_kind {
1006 TxContextKind::None => (),
1007 TxContextKind::Mutable | TxContextKind::Immutable => {
1008 serialized_arguments.push(context.tx_context.borrow().to_bcs_legacy_context());
1009 }
1010 }
1011 let mut result = context
1013 .execute_function_bypass_visibility(
1014 module_id,
1015 function,
1016 type_arguments,
1017 serialized_arguments,
1018 trace_builder_opt,
1019 )
1020 .map_err(|e| context.convert_vm_error(e))?;
1021
1022 if tx_context_kind == TxContextKind::Mutable {
1030 let Some((_, ctx_bytes, _)) = result.mutable_reference_outputs.pop() else {
1031 invariant_violation!("Missing TxContext in reference outputs");
1032 };
1033 let updated_ctx: MoveLegacyTxContext = bcs::from_bytes(&ctx_bytes).map_err(|e| {
1034 ExecutionError::invariant_violation(format!(
1035 "Unable to deserialize TxContext bytes. {e}"
1036 ))
1037 })?;
1038 context.tx_context.borrow_mut().update_state(updated_ctx)?;
1039 }
1040 Ok(result)
1041 }
1042
1043 fn publish_and_verify_modules(
1044 context: &mut ExecutionContext<'_, '_, '_>,
1045 package_id: ObjectID,
1046 modules: &[CompiledModule],
1047 ) -> Result<(), ExecutionError> {
1048 let binary_version = context.protocol_config.move_binary_format_version();
1050 let new_module_bytes: Vec<_> = modules
1051 .iter()
1052 .map(|m| {
1053 let mut bytes = Vec::new();
1054 let version = if binary_version > VERSION_6 {
1055 m.version
1056 } else {
1057 VERSION_6
1058 };
1059 m.serialize_with_version(version, &mut bytes).unwrap();
1060 bytes
1061 })
1062 .collect();
1063 context
1064 .publish_module_bundle(new_module_bytes, AccountAddress::from(package_id))
1065 .map_err(|e| context.convert_vm_error(e))?;
1066
1067 for module in modules {
1069 sui_verifier::verifier::sui_verify_module_unmetered(
1072 module,
1073 &BTreeMap::new(),
1074 &context
1075 .protocol_config
1076 .verifier_config(None),
1077 )?;
1078 }
1079
1080 Ok(())
1081 }
1082
1083 fn init_modules<'a, Mode: ExecutionMode>(
1084 context: &mut ExecutionContext<'_, '_, '_>,
1085 argument_updates: &mut Mode::ArgumentUpdates,
1086 modules: impl IntoIterator<Item = &'a CompiledModule>,
1087 trace_builder_opt: &mut Option<MoveTraceBuilder>,
1088 ) -> Result<(), ExecutionError> {
1089 let modules_to_init = modules.into_iter().filter_map(|module| {
1090 for fdef in &module.function_defs {
1091 let fhandle = module.function_handle_at(fdef.function);
1092 let fname = module.identifier_at(fhandle.name);
1093 if fname == INIT_FN_NAME {
1094 return Some(module.self_id());
1095 }
1096 }
1097 None
1098 });
1099
1100 for module_id in modules_to_init {
1101 trace_utils::trace_move_call_start(trace_builder_opt);
1102 let return_values = execute_move_call::<Mode>(
1103 context,
1104 argument_updates,
1105 &module_id,
1109 &module_id,
1110 INIT_FN_NAME,
1111 vec![],
1112 vec![],
1113 true,
1114 trace_builder_opt,
1115 )?;
1116
1117 assert_invariant!(
1118 return_values.is_empty(),
1119 "init should not have return values"
1120 );
1121
1122 trace_utils::trace_move_call_end(trace_builder_opt);
1123 }
1124
1125 Ok(())
1126 }
1127
1128 #[derive(PartialEq, Eq, Clone, Copy)]
1134 enum FunctionKind {
1135 PrivateEntry,
1136 PublicEntry,
1137 NonEntry,
1138 Init,
1139 }
1140
1141 enum ValueKind {
1143 Object {
1144 type_: MoveObjectType,
1145 has_public_transfer: bool,
1146 },
1147 Raw(Type, AbilitySet),
1148 }
1149
1150 struct LoadedFunctionInfo {
1151 kind: FunctionKind,
1153 signature: LoadedFunctionInstantiation,
1155 return_value_kinds: Vec<ValueKind>,
1157 index: FunctionDefinitionIndex,
1159 last_instr: CodeOffset,
1161 }
1162
1163 fn check_visibility_and_signature<Mode: ExecutionMode>(
1168 context: &mut ExecutionContext<'_, '_, '_>,
1169 module_id: &ModuleId,
1170 function: &IdentStr,
1171 type_arguments: &[Type],
1172 from_init: bool,
1173 ) -> Result<LoadedFunctionInfo, ExecutionError> {
1174 if from_init {
1175 let result = context.load_function(module_id, function, type_arguments);
1176 assert_invariant!(
1177 result.is_ok(),
1178 "The modules init should be able to be loaded"
1179 );
1180 }
1181 let no_new_packages = vec![];
1182 let data_store = SuiDataStore::new(&context.linkage_view, &no_new_packages);
1183 let module = context
1184 .vm
1185 .get_runtime()
1186 .load_module(module_id, &data_store)
1187 .map_err(|e| context.convert_vm_error(e))?;
1188 let Some((index, fdef)) = module
1189 .function_defs
1190 .iter()
1191 .enumerate()
1192 .find(|(_index, fdef)| {
1193 module.identifier_at(module.function_handle_at(fdef.function).name) == function
1194 })
1195 else {
1196 return Err(ExecutionError::new_with_source(
1197 ExecutionErrorKind::FunctionNotFound,
1198 format!(
1199 "Could not resolve function '{}' in module {}",
1200 function, &module_id,
1201 ),
1202 ));
1203 };
1204
1205 if !from_init && function == INIT_FN_NAME && context.protocol_config.ban_entry_init() {
1207 return Err(ExecutionError::new_with_source(
1208 ExecutionErrorKind::NonEntryFunctionInvoked,
1209 "Cannot call 'init'",
1210 ));
1211 }
1212
1213 let last_instr: CodeOffset = fdef
1214 .code
1215 .as_ref()
1216 .map(|code| code.code.len() - 1)
1217 .unwrap_or(0) as CodeOffset;
1218 let function_kind = match (fdef.visibility, fdef.is_entry) {
1219 (Visibility::Private | Visibility::Friend, true) => FunctionKind::PrivateEntry,
1220 (Visibility::Public, true) => FunctionKind::PublicEntry,
1221 (Visibility::Public, false) => FunctionKind::NonEntry,
1222 (Visibility::Private, false) if from_init => {
1223 assert_invariant!(
1224 function == INIT_FN_NAME,
1225 "module init specified non-init function"
1226 );
1227 FunctionKind::Init
1228 }
1229 (Visibility::Private | Visibility::Friend, false)
1230 if Mode::allow_arbitrary_function_calls() =>
1231 {
1232 FunctionKind::NonEntry
1233 }
1234 (Visibility::Private | Visibility::Friend, false) => {
1235 return Err(ExecutionError::new_with_source(
1236 ExecutionErrorKind::NonEntryFunctionInvoked,
1237 "Can only call `entry` or `public` functions",
1238 ));
1239 }
1240 };
1241 let signature = context
1242 .load_function(module_id, function, type_arguments)
1243 .map_err(|e| context.convert_vm_error(e))?;
1244 let signature =
1245 subst_signature(signature, type_arguments).map_err(|e| context.convert_vm_error(e))?;
1246 let return_value_kinds = match function_kind {
1247 FunctionKind::Init => {
1248 assert_invariant!(
1249 signature.return_.is_empty(),
1250 "init functions must have no return values"
1251 );
1252 vec![]
1253 }
1254 FunctionKind::PrivateEntry | FunctionKind::PublicEntry | FunctionKind::NonEntry => {
1255 check_non_entry_signature::<Mode>(context, module_id, function, &signature)?
1256 }
1257 };
1258 if context.protocol_config.private_generics_verifier_v2() {
1259 check_private_generics_v2(module_id, function)?;
1260 } else {
1261 check_private_generics(module_id, function)?;
1262 }
1263 Ok(LoadedFunctionInfo {
1264 kind: function_kind,
1265 signature,
1266 return_value_kinds,
1267 index: FunctionDefinitionIndex(index as u16),
1268 last_instr,
1269 })
1270 }
1271
1272 pub fn subst_signature(
1274 signature: LoadedFunctionInstantiation,
1275 type_arguments: &[Type],
1276 ) -> VMResult<LoadedFunctionInstantiation> {
1277 let LoadedFunctionInstantiation {
1278 parameters,
1279 return_,
1280 instruction_length,
1281 definition_index,
1282 } = signature;
1283 let parameters = parameters
1284 .into_iter()
1285 .map(|ty| ty.subst(type_arguments))
1286 .collect::<PartialVMResult<Vec<_>>>()
1287 .map_err(|err| err.finish(Location::Undefined))?;
1288 let return_ = return_
1289 .into_iter()
1290 .map(|ty| ty.subst(type_arguments))
1291 .collect::<PartialVMResult<Vec<_>>>()
1292 .map_err(|err| err.finish(Location::Undefined))?;
1293 Ok(LoadedFunctionInstantiation {
1294 parameters,
1295 return_,
1296 instruction_length,
1297 definition_index,
1298 })
1299 }
1300
1301 fn check_non_entry_signature<Mode: ExecutionMode>(
1304 context: &mut ExecutionContext<'_, '_, '_>,
1305 _module_id: &ModuleId,
1306 _function: &IdentStr,
1307 signature: &LoadedFunctionInstantiation,
1308 ) -> Result<Vec<ValueKind>, ExecutionError> {
1309 signature
1310 .return_
1311 .iter()
1312 .enumerate()
1313 .map(|(idx, return_type)| {
1314 let return_type = match return_type {
1315 Type::Reference(inner) | Type::MutableReference(inner)
1317 if Mode::allow_arbitrary_values() =>
1318 {
1319 inner
1320 }
1321 Type::Reference(_) | Type::MutableReference(_) => {
1322 return Err(ExecutionError::from_kind(
1323 ExecutionErrorKind::InvalidPublicFunctionReturnType { idx: idx as u16 },
1324 ));
1325 }
1326 t => t,
1327 };
1328 let abilities = context.get_type_abilities(return_type)?;
1329 Ok(match return_type {
1330 Type::MutableReference(_) | Type::Reference(_) => unreachable!(),
1331 Type::TyParam(_) => {
1332 invariant_violation!("TyParam should have been substituted")
1333 }
1334 Type::Datatype(_) | Type::DatatypeInstantiation(_) if abilities.has_key() => {
1335 let type_tag = context
1336 .vm
1337 .get_runtime()
1338 .get_type_tag(return_type)
1339 .map_err(|e| context.convert_vm_error(e))?;
1340 let TypeTag::Struct(struct_tag) = type_tag else {
1341 invariant_violation!("Struct type make a non struct type tag")
1342 };
1343 ValueKind::Object {
1344 type_: MoveObjectType::from(*struct_tag),
1345 has_public_transfer: abilities.has_store(),
1346 }
1347 }
1348 Type::Datatype(_)
1349 | Type::DatatypeInstantiation(_)
1350 | Type::Bool
1351 | Type::U8
1352 | Type::U64
1353 | Type::U128
1354 | Type::Address
1355 | Type::Signer
1356 | Type::Vector(_)
1357 | Type::U16
1358 | Type::U32
1359 | Type::U256 => ValueKind::Raw(return_type.clone(), abilities),
1360 })
1361 })
1362 .collect()
1363 }
1364
1365 pub fn check_private_generics(
1366 module_id: &ModuleId,
1367 function: &IdentStr,
1368 ) -> Result<(), ExecutionError> {
1369 let module_ident = (module_id.address(), module_id.name());
1370 if module_ident == (&SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
1371 return Err(ExecutionError::new_with_source(
1372 ExecutionErrorKind::NonEntryFunctionInvoked,
1373 format!("Cannot directly call functions in sui::{}", EVENT_MODULE),
1374 ));
1375 }
1376
1377 if module_ident == (&SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE)
1378 && PRIVATE_TRANSFER_FUNCTIONS.contains(&function)
1379 {
1380 let msg = format!(
1381 "Cannot directly call sui::{m}::{f}. \
1382 Use the public variant instead, sui::{m}::public_{f}",
1383 m = TRANSFER_MODULE,
1384 f = function
1385 );
1386 return Err(ExecutionError::new_with_source(
1387 ExecutionErrorKind::NonEntryFunctionInvoked,
1388 msg,
1389 ));
1390 }
1391
1392 Ok(())
1393 }
1394
1395 pub fn check_private_generics_v2(
1396 callee_package: &ModuleId,
1397 callee_function: &IdentStr,
1398 ) -> Result<(), ExecutionError> {
1399 let callee_address = *callee_package.address();
1400 let callee_module = callee_package.name();
1401 let callee = (callee_address, callee_module, callee_function);
1402 let Some((_f, internal_type_parameters)) = private_generics_verifier_v2::FUNCTIONS_TO_CHECK
1403 .iter()
1404 .find(|(f, _)| &callee == f)
1405 else {
1406 return Ok(());
1407 };
1408 let Some((internal_idx, _)) = internal_type_parameters
1411 .iter()
1412 .enumerate()
1413 .find(|(_, is_internal)| **is_internal)
1414 else {
1415 return Ok(());
1417 };
1418 let callee_package_name =
1419 private_generics_verifier_v2::callee_package_name(&callee_address);
1420 let help = private_generics_verifier_v2::help_message(
1421 &callee_address,
1422 callee_module,
1423 callee_function,
1424 );
1425 let msg = format!(
1426 "Cannot directly call function '{}::{}::{}' since type parameter #{} can \
1427 only be instantiated with types defined within the caller's module.{}",
1428 callee_package_name, callee_module, callee_function, internal_idx, help,
1429 );
1430 Err(ExecutionError::new_with_source(
1431 ExecutionErrorKind::NonEntryFunctionInvoked,
1432 msg,
1433 ))
1434 }
1435
1436 type ArgInfo = (
1437 TxContextKind,
1438 Vec<(LocalIndex, ValueKind)>,
1440 Vec<Vec<u8>>,
1441 );
1442
1443 fn build_move_args<Mode: ExecutionMode>(
1446 context: &mut ExecutionContext<'_, '_, '_>,
1447 _module_id: &ModuleId,
1448 function: &IdentStr,
1449 function_kind: FunctionKind,
1450 signature: &LoadedFunctionInstantiation,
1451 args: &[Arg],
1452 ) -> Result<ArgInfo, ExecutionError> {
1453 let parameters = &signature.parameters;
1455 let tx_ctx_kind = match parameters.last() {
1456 Some(t) => is_tx_context(context, t)?,
1457 None => TxContextKind::None,
1458 };
1459 let has_one_time_witness = function_kind == FunctionKind::Init && parameters.len() == 2;
1463 let has_tx_context = tx_ctx_kind != TxContextKind::None;
1464 let num_args = args.len() + (has_one_time_witness as usize) + (has_tx_context as usize);
1465 if num_args != parameters.len() {
1466 return Err(ExecutionError::new_with_source(
1467 ExecutionErrorKind::ArityMismatch,
1468 format!(
1469 "Expected {:?} argument{} calling function '{}', but found {:?}",
1470 parameters.len(),
1471 if parameters.len() == 1 { "" } else { "s" },
1472 function,
1473 num_args
1474 ),
1475 ));
1476 }
1477
1478 let mut by_mut_ref = vec![];
1480 let mut serialized_args = Vec::with_capacity(num_args);
1481 if has_one_time_witness {
1485 let bcs_true_value = bcs::to_bytes(&true).unwrap();
1488 serialized_args.push(bcs_true_value)
1489 }
1490 for ((idx, arg), param_ty) in args.iter().copied().enumerate().zip(parameters) {
1491 let (value, non_ref_param_ty): (Value, &Type) = match param_ty {
1492 Type::MutableReference(inner) => {
1493 let value = context.borrow_arg_mut(idx, arg)?;
1494 let object_info = if let Value::Object(ObjectValue {
1495 type_,
1496 has_public_transfer,
1497 ..
1498 }) = &value
1499 {
1500 let type_tag = context
1501 .vm
1502 .get_runtime()
1503 .get_type_tag(type_)
1504 .map_err(|e| context.convert_vm_error(e))?;
1505 let TypeTag::Struct(struct_tag) = type_tag else {
1506 invariant_violation!("Struct type make a non struct type tag")
1507 };
1508 let type_ = (*struct_tag).into();
1509 ValueKind::Object {
1510 type_,
1511 has_public_transfer: *has_public_transfer,
1512 }
1513 } else {
1514 let abilities = context.get_type_abilities(inner)?;
1515 ValueKind::Raw((**inner).clone(), abilities)
1516 };
1517 by_mut_ref.push((idx as LocalIndex, object_info));
1518 (value, inner)
1519 }
1520 Type::Reference(inner) => (context.borrow_arg(idx, arg, param_ty)?, inner),
1521 t => {
1522 let value = context.by_value_arg(CommandKind::MoveCall, idx, arg)?;
1523 (value, t)
1524 }
1525 };
1526 if matches!(
1527 function_kind,
1528 FunctionKind::PrivateEntry | FunctionKind::Init
1529 ) && value.was_used_in_non_entry_move_call()
1530 {
1531 return Err(command_argument_error(
1532 CommandArgumentError::InvalidArgumentToPrivateEntryFunction,
1533 idx,
1534 ));
1535 }
1536 check_param_type::<Mode>(context, idx, &value, non_ref_param_ty)?;
1537 let bytes = {
1538 let mut v = vec![];
1539 value.write_bcs_bytes(&mut v, None)?;
1540 v
1541 };
1542 serialized_args.push(bytes);
1543 }
1544 Ok((tx_ctx_kind, by_mut_ref, serialized_args))
1545 }
1546
1547 fn check_param_type<Mode: ExecutionMode>(
1549 context: &mut ExecutionContext<'_, '_, '_>,
1550 idx: usize,
1551 value: &Value,
1552 param_ty: &Type,
1553 ) -> Result<(), ExecutionError> {
1554 match value {
1555 Value::Raw(RawValueType::Any, bytes) if Mode::allow_arbitrary_values() => {
1558 if let Some(bound) = amplification_bound_::<Mode>(context, param_ty)? {
1559 let bound = context.size_bound_raw(bound);
1560 return ensure_serialized_size(bytes.len() as u64, bound);
1561 } else {
1562 return Ok(());
1563 }
1564 }
1565 Value::Raw(RawValueType::Any, bytes) => {
1569 let Some(layout) = primitive_serialization_layout(context, param_ty)? else {
1570 let msg = format!(
1571 "Non-primitive argument at index {}. If it is an object, it must be \
1572 populated by an object",
1573 idx,
1574 );
1575 return Err(ExecutionError::new_with_source(
1576 ExecutionErrorKind::command_argument_error(
1577 CommandArgumentError::InvalidUsageOfPureArg,
1578 idx as u16,
1579 ),
1580 msg,
1581 ));
1582 };
1583 bcs_argument_validate(bytes, idx as u16, layout)?;
1584 return Ok(());
1585 }
1586 Value::Raw(RawValueType::Loaded { ty, abilities, .. }, _) => {
1587 assert_invariant!(
1588 Mode::allow_arbitrary_values() || !abilities.has_key(),
1589 "Raw value should never be an object"
1590 );
1591 if ty != param_ty {
1592 return Err(command_argument_error(
1593 CommandArgumentError::TypeMismatch,
1594 idx,
1595 ));
1596 }
1597 }
1598 Value::Object(obj) => {
1599 let ty = &obj.type_;
1600 if ty != param_ty {
1601 return Err(command_argument_error(
1602 CommandArgumentError::TypeMismatch,
1603 idx,
1604 ));
1605 }
1606 }
1607 Value::Receiving(_, _, assigned_type) => {
1608 if let Some(assigned_type) = assigned_type
1610 && assigned_type != param_ty
1611 {
1612 return Err(command_argument_error(
1613 CommandArgumentError::TypeMismatch,
1614 idx,
1615 ));
1616 }
1617
1618 let Type::DatatypeInstantiation(inst) = param_ty else {
1620 return Err(command_argument_error(
1621 CommandArgumentError::TypeMismatch,
1622 idx,
1623 ));
1624 };
1625 let (sidx, targs) = &**inst;
1626 let Some(s) = context.vm.get_runtime().get_type(*sidx) else {
1627 invariant_violation!("sui::transfer::Receiving struct not found in session")
1628 };
1629 let resolved_struct = get_datatype_ident(&s);
1630
1631 if resolved_struct != RESOLVED_RECEIVING_STRUCT || targs.len() != 1 {
1632 return Err(command_argument_error(
1633 CommandArgumentError::TypeMismatch,
1634 idx,
1635 ));
1636 }
1637 }
1638 }
1639 Ok(())
1640 }
1641
1642 fn to_identifier(
1643 context: &mut ExecutionContext<'_, '_, '_>,
1644 ident: String,
1645 ) -> Result<Identifier, ExecutionError> {
1646 if context.protocol_config.validate_identifier_inputs() {
1647 Identifier::new(ident).map_err(|e| {
1648 ExecutionError::new_with_source(
1649 ExecutionErrorKind::VMInvariantViolation,
1650 e.to_string(),
1651 )
1652 })
1653 } else {
1654 Ok(unsafe { Identifier::new_unchecked(ident) })
1656 }
1657 }
1658
1659 fn to_type_tag(
1668 context: &mut ExecutionContext<'_, '_, '_>,
1669 type_input: TypeInput,
1670 idx: usize,
1671 ) -> Result<TypeTag, ExecutionError> {
1672 let type_tag_no_def_ids = to_type_tag_(context, type_input, idx)?;
1673 if context
1674 .protocol_config
1675 .resolve_type_input_ids_to_defining_id()
1676 {
1677 let ix = if context
1678 .protocol_config
1679 .better_adapter_type_resolution_errors()
1680 {
1681 idx
1682 } else {
1683 0
1684 };
1685
1686 let ty = context
1687 .load_type(&type_tag_no_def_ids)
1688 .map_err(|e| context.convert_type_argument_error(ix, e))?;
1689 context.get_type_tag(&ty)
1690 } else {
1691 Ok(type_tag_no_def_ids)
1692 }
1693 }
1694
1695 fn to_type_tag_(
1696 context: &mut ExecutionContext<'_, '_, '_>,
1697 type_input: TypeInput,
1698 idx: usize,
1699 ) -> Result<TypeTag, ExecutionError> {
1700 use TypeInput as I;
1701 use TypeTag as T;
1702 Ok(match type_input {
1703 I::Bool => T::Bool,
1704 I::U8 => T::U8,
1705 I::U16 => T::U16,
1706 I::U32 => T::U32,
1707 I::U64 => T::U64,
1708 I::U128 => T::U128,
1709 I::U256 => T::U256,
1710 I::Address => T::Address,
1711 I::Signer => T::Signer,
1712 I::Vector(t) => T::Vector(Box::new(to_type_tag_(context, *t, idx)?)),
1713 I::Struct(s) => {
1714 let StructInput {
1715 address,
1716 module,
1717 name,
1718 type_params,
1719 } = *s;
1720 let type_params = type_params
1721 .into_iter()
1722 .map(|t| to_type_tag_(context, t, idx))
1723 .collect::<Result<_, _>>()?;
1724 let (module, name) = resolve_datatype_names(context, address, module, name, idx)?;
1725 T::Struct(Box::new(StructTag {
1726 address,
1727 module,
1728 name,
1729 type_params,
1730 }))
1731 }
1732 })
1733 }
1734
1735 fn resolve_datatype_names(
1736 context: &ExecutionContext<'_, '_, '_>,
1737 addr: AccountAddress,
1738 module: String,
1739 name: String,
1740 idx: usize,
1741 ) -> Result<(Identifier, Identifier), ExecutionError> {
1742 let validate_identifiers = context.protocol_config.validate_identifier_inputs();
1743 let better_resolution_errors = context
1744 .protocol_config
1745 .better_adapter_type_resolution_errors();
1746
1747 let to_ident = |s| {
1748 if validate_identifiers {
1749 Identifier::new(s).map_err(|e| {
1750 ExecutionError::new_with_source(
1751 ExecutionErrorKind::VMInvariantViolation,
1752 e.to_string(),
1753 )
1754 })
1755 } else {
1756 unsafe { Ok(Identifier::new_unchecked(s)) }
1759 }
1760 };
1761
1762 let module_ident = to_ident(module.clone())?;
1763 let name_ident = to_ident(name.clone())?;
1764
1765 if better_resolution_errors
1766 && context
1767 .linkage_view
1768 .get_package(&addr.into())
1769 .ok()
1770 .flatten()
1771 .is_none_or(|pkg| !pkg.type_origin_map().contains_key(&(module, name)))
1772 {
1773 return Err(ExecutionError::from_kind(
1774 ExecutionErrorKind::TypeArgumentError {
1775 argument_idx: idx as u16,
1776 kind: TypeArgumentError::TypeNotFound,
1777 },
1778 ));
1779 }
1780
1781 Ok((module_ident, name_ident))
1782 }
1783
1784 fn get_datatype_ident(s: &CachedDatatype) -> (&AccountAddress, &IdentStr, &IdentStr) {
1785 let module_id = &s.defining_id;
1786 let struct_name = &s.name;
1787 (
1788 module_id.address(),
1789 module_id.name(),
1790 struct_name.as_ident_str(),
1791 )
1792 }
1793
1794 pub fn is_tx_context(
1798 context: &mut ExecutionContext<'_, '_, '_>,
1799 t: &Type,
1800 ) -> Result<TxContextKind, ExecutionError> {
1801 let (is_mut, inner) = match t {
1802 Type::MutableReference(inner) => (true, inner),
1803 Type::Reference(inner) => (false, inner),
1804 _ => return Ok(TxContextKind::None),
1805 };
1806 let Type::Datatype(idx) = &**inner else {
1807 return Ok(TxContextKind::None);
1808 };
1809 let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1810 invariant_violation!("Loaded struct not found")
1811 };
1812 let (module_addr, module_name, struct_name) = get_datatype_ident(&s);
1813 let is_tx_context_type = module_addr == &SUI_FRAMEWORK_ADDRESS
1814 && module_name == TX_CONTEXT_MODULE_NAME
1815 && struct_name == TX_CONTEXT_STRUCT_NAME;
1816 Ok(if is_tx_context_type {
1817 if is_mut {
1818 TxContextKind::Mutable
1819 } else {
1820 TxContextKind::Immutable
1821 }
1822 } else {
1823 TxContextKind::None
1824 })
1825 }
1826
1827 fn primitive_serialization_layout(
1829 context: &mut ExecutionContext<'_, '_, '_>,
1830 param_ty: &Type,
1831 ) -> Result<Option<PrimitiveArgumentLayout>, ExecutionError> {
1832 Ok(match param_ty {
1833 Type::Signer => return Ok(None),
1834 Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => {
1835 invariant_violation!("references and type parameters should be checked elsewhere")
1836 }
1837 Type::Bool => Some(PrimitiveArgumentLayout::Bool),
1838 Type::U8 => Some(PrimitiveArgumentLayout::U8),
1839 Type::U16 => Some(PrimitiveArgumentLayout::U16),
1840 Type::U32 => Some(PrimitiveArgumentLayout::U32),
1841 Type::U64 => Some(PrimitiveArgumentLayout::U64),
1842 Type::U128 => Some(PrimitiveArgumentLayout::U128),
1843 Type::U256 => Some(PrimitiveArgumentLayout::U256),
1844 Type::Address => Some(PrimitiveArgumentLayout::Address),
1845
1846 Type::Vector(inner) => {
1847 let info_opt = primitive_serialization_layout(context, inner)?;
1848 info_opt.map(|layout| PrimitiveArgumentLayout::Vector(Box::new(layout)))
1849 }
1850 Type::DatatypeInstantiation(inst) => {
1851 let (idx, targs) = &**inst;
1852 let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1853 invariant_violation!("Loaded struct not found")
1854 };
1855 let resolved_struct = get_datatype_ident(&s);
1856 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
1858 let info_opt = primitive_serialization_layout(context, &targs[0])?;
1859 info_opt.map(|layout| PrimitiveArgumentLayout::Option(Box::new(layout)))
1860 } else {
1861 None
1862 }
1863 }
1864 Type::Datatype(idx) => {
1865 let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1866 invariant_violation!("Loaded struct not found")
1867 };
1868 let resolved_struct = get_datatype_ident(&s);
1869 if resolved_struct == RESOLVED_SUI_ID {
1870 Some(PrimitiveArgumentLayout::Address)
1871 } else if resolved_struct == RESOLVED_ASCII_STR {
1872 Some(PrimitiveArgumentLayout::Ascii)
1873 } else if resolved_struct == RESOLVED_UTF8_STR {
1874 Some(PrimitiveArgumentLayout::UTF8)
1875 } else {
1876 None
1877 }
1878 }
1879 })
1880 }
1881
1882 fn amplification_bound<Mode: ExecutionMode>(
1887 context: &mut ExecutionContext<'_, '_, '_>,
1888 param_ty: &Type,
1889 abilities: &OnceCell<AbilitySet>,
1890 ) -> Result<Option<u64>, ExecutionError> {
1891 if context.protocol_config.max_ptb_value_size_v2() {
1892 if abilities.get().is_none() {
1893 abilities
1894 .set(context.get_type_abilities(param_ty)?)
1895 .unwrap();
1896 }
1897 if !abilities.get().unwrap().has_copy() {
1898 return Ok(None);
1899 }
1900 }
1901 amplification_bound_::<Mode>(context, param_ty)
1902 }
1903
1904 fn amplification_bound_<Mode: ExecutionMode>(
1905 context: &mut ExecutionContext<'_, '_, '_>,
1906 param_ty: &Type,
1907 ) -> Result<Option<u64>, ExecutionError> {
1908 if Mode::packages_are_predefined() {
1910 return Ok(None);
1911 }
1912
1913 let Some(bound) = context.protocol_config.max_ptb_value_size_as_option() else {
1914 return Ok(None);
1915 };
1916
1917 fn amplification(prim_layout: &PrimitiveArgumentLayout) -> Result<u64, ExecutionError> {
1918 use PrimitiveArgumentLayout as PAL;
1919 Ok(match prim_layout {
1920 PAL::Option(inner_layout) => 1u64 + amplification(inner_layout)?,
1921 PAL::Vector(inner_layout) => amplification(inner_layout)?,
1922 PAL::Ascii | PAL::UTF8 => 2,
1923 PAL::Bool | PAL::U8 | PAL::U16 | PAL::U32 | PAL::U64 => 1,
1924 PAL::U128 | PAL::U256 | PAL::Address => 2,
1925 })
1926 }
1927
1928 let mut amplification = match primitive_serialization_layout(context, param_ty)? {
1929 None => context.protocol_config.max_move_value_depth(),
1932 Some(layout) => amplification(&layout)?,
1933 };
1934
1935 debug_assert!(amplification != 0);
1937 debug_assert!(
1940 context.protocol_config.max_move_value_depth()
1941 >= context.protocol_config.max_type_argument_depth() as u64
1942 );
1943 assert_ne!(context.protocol_config.max_move_value_depth(), 0);
1944 if amplification == 0 {
1945 amplification = context.protocol_config.max_move_value_depth();
1946 }
1947 Ok(Some(bound / amplification))
1948 }
1949
1950 #[derive(Debug)]
1958 pub enum PrimitiveArgumentLayout {
1959 Option(Box<PrimitiveArgumentLayout>),
1961 Vector(Box<PrimitiveArgumentLayout>),
1963 Ascii,
1965 UTF8,
1967 Bool,
1969 U8,
1970 U16,
1971 U32,
1972 U64,
1973 U128,
1974 U256,
1975 Address,
1976 }
1977
1978 impl PrimitiveArgumentLayout {
1979 pub fn bcs_only(&self) -> bool {
1983 match self {
1984 PrimitiveArgumentLayout::Option(_)
1986 | PrimitiveArgumentLayout::Ascii
1987 | PrimitiveArgumentLayout::UTF8 => false,
1988 PrimitiveArgumentLayout::Bool
1990 | PrimitiveArgumentLayout::U8
1991 | PrimitiveArgumentLayout::U16
1992 | PrimitiveArgumentLayout::U32
1993 | PrimitiveArgumentLayout::U64
1994 | PrimitiveArgumentLayout::U128
1995 | PrimitiveArgumentLayout::U256
1996 | PrimitiveArgumentLayout::Address => true,
1997 PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
1999 }
2000 }
2001 }
2002
2003 pub fn bcs_argument_validate(
2007 bytes: &[u8],
2008 idx: u16,
2009 layout: PrimitiveArgumentLayout,
2010 ) -> Result<(), ExecutionError> {
2011 bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
2012 ExecutionError::new_with_source(
2013 ExecutionErrorKind::command_argument_error(
2014 CommandArgumentError::InvalidBCSBytes,
2015 idx,
2016 ),
2017 format!("Function expects {layout} but provided argument's value does not match",),
2018 )
2019 })
2020 }
2021
2022 impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
2023 type Value = ();
2024 fn deserialize<D: serde::de::Deserializer<'d>>(
2025 self,
2026 deserializer: D,
2027 ) -> Result<Self::Value, D::Error> {
2028 use serde::de::Error;
2029 match self {
2030 PrimitiveArgumentLayout::Ascii => {
2031 let s: &str = serde::Deserialize::deserialize(deserializer)?;
2032 if !s.is_ascii() {
2033 Err(D::Error::custom("not an ascii string"))
2034 } else {
2035 Ok(())
2036 }
2037 }
2038 PrimitiveArgumentLayout::UTF8 => {
2039 deserializer.deserialize_string(serde::de::IgnoredAny)?;
2040 Ok(())
2041 }
2042 PrimitiveArgumentLayout::Option(layout) => {
2043 deserializer.deserialize_option(OptionElementVisitor(layout))
2044 }
2045 PrimitiveArgumentLayout::Vector(layout) => {
2046 deserializer.deserialize_seq(VectorElementVisitor(layout))
2047 }
2048 PrimitiveArgumentLayout::Bool => {
2051 deserializer.deserialize_bool(serde::de::IgnoredAny)?;
2052 Ok(())
2053 }
2054 PrimitiveArgumentLayout::U8 => {
2055 deserializer.deserialize_u8(serde::de::IgnoredAny)?;
2056 Ok(())
2057 }
2058 PrimitiveArgumentLayout::U16 => {
2059 deserializer.deserialize_u16(serde::de::IgnoredAny)?;
2060 Ok(())
2061 }
2062 PrimitiveArgumentLayout::U32 => {
2063 deserializer.deserialize_u32(serde::de::IgnoredAny)?;
2064 Ok(())
2065 }
2066 PrimitiveArgumentLayout::U64 => {
2067 deserializer.deserialize_u64(serde::de::IgnoredAny)?;
2068 Ok(())
2069 }
2070 PrimitiveArgumentLayout::U128 => {
2071 deserializer.deserialize_u128(serde::de::IgnoredAny)?;
2072 Ok(())
2073 }
2074 PrimitiveArgumentLayout::U256 => {
2075 U256::deserialize(deserializer)?;
2076 Ok(())
2077 }
2078 PrimitiveArgumentLayout::Address => {
2079 SuiAddress::deserialize(deserializer)?;
2080 Ok(())
2081 }
2082 }
2083 }
2084 }
2085
2086 struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2087
2088 impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
2089 type Value = ();
2090
2091 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2092 formatter.write_str("Vector")
2093 }
2094
2095 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
2096 where
2097 A: serde::de::SeqAccess<'d>,
2098 {
2099 while seq.next_element_seed(self.0)?.is_some() {}
2100 Ok(())
2101 }
2102 }
2103
2104 struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
2105
2106 impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
2107 type Value = ();
2108
2109 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2110 formatter.write_str("Option")
2111 }
2112
2113 fn visit_none<E>(self) -> Result<Self::Value, E>
2114 where
2115 E: serde::de::Error,
2116 {
2117 Ok(())
2118 }
2119
2120 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
2121 where
2122 D: serde::Deserializer<'d>,
2123 {
2124 self.0.deserialize(deserializer)
2125 }
2126 }
2127
2128 impl fmt::Display for PrimitiveArgumentLayout {
2129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2130 match self {
2131 PrimitiveArgumentLayout::Vector(inner) => {
2132 write!(f, "vector<{inner}>")
2133 }
2134 PrimitiveArgumentLayout::Option(inner) => {
2135 write!(f, "std::option::Option<{inner}>")
2136 }
2137 PrimitiveArgumentLayout::Ascii => {
2138 write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
2139 }
2140 PrimitiveArgumentLayout::UTF8 => {
2141 write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
2142 }
2143 PrimitiveArgumentLayout::Bool => write!(f, "bool"),
2144 PrimitiveArgumentLayout::U8 => write!(f, "u8"),
2145 PrimitiveArgumentLayout::U16 => write!(f, "u16"),
2146 PrimitiveArgumentLayout::U32 => write!(f, "u32"),
2147 PrimitiveArgumentLayout::U64 => write!(f, "u64"),
2148 PrimitiveArgumentLayout::U128 => write!(f, "u128"),
2149 PrimitiveArgumentLayout::U256 => write!(f, "u256"),
2150 PrimitiveArgumentLayout::Address => write!(f, "address"),
2151 }
2152 }
2153 }
2154}