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