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