sui_adapter_v3/programmable_transactions/
execution.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4pub 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        // execute commands
159        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                // We still need to record the loaded child objects for replay
167                let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
168                // we do not save the wrapped objects since on error, they should not be modified
169                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        // Save loaded objects table in case we fail in post execution
178        let object_runtime: &ObjectRuntime = context.object_runtime()?;
179        // We still need to record the loaded child objects for replay
180        // Record the objects loaded at runtime (dynamic fields + received) for
181        // storage rebate calculation.
182        let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
183        // We record what objects were contained in at the start of the transaction
184        // for expensive invariant checks
185        let wrapped_object_containers = object_runtime.wrapped_object_containers();
186        // We record the generated object IDs for expensive invariant checks
187        let generated_object_ids = object_runtime.generated_object_ids();
188
189        // apply changes
190        let finished = context.finish::<Mode>();
191        // Save loaded objects for debug. We dont want to lose the info
192        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    /// Execute a single command
200    #[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                // BCS layout for any empty vector should be the same
239                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                    // If no tag specified, it _must_ be an object
272                    None => {
273                        // empty args covered above
274                        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                        // safe because we are propagating the coin type, and relying on the internal
365                        // invariant that coin values have a coin type
366                        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                // Convert type arguments to `Type`s
456                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                    /* is_init */ 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    /// Execute a single Move call
516    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        // check that the function is either an entry function or a valid public function
528        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        // build the arguments, storing meta data about by-mut-ref args
542        let (tx_context_kind, by_mut_ref, serialized_arguments) =
543            build_move_args::<Mode>(context, runtime_id, function, kind, &signature, &arguments)?;
544        // invoke the VM
545        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        // save the link context because calls to `make_value` below can set new ones, and we don't want
569        // it to be clobbered.
570        let saved_linkage = context.linkage_view.steal_linkage();
571        // write back mutable inputs. We also update if they were used in non entry Move calls
572        // though we do not care for immutable usages of objects or other values
573        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                // only non entry functions have return values
613                make_value(
614                    context, kind, bytes, /* used_in_non_entry_move_call */ 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    /// Publish Move modules and call the init functions.  Returns an `UpgradeCap` for the newly
648    /// published package on success.
649    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        // It should be fine that this does not go through ExecutionContext::fresh_id since the Move
667        // runtime does not to know about new packages created, since Move objects and Move packages
668        // cannot interact
669        let runtime_id = if Mode::packages_are_predefined() {
670            // do not calculate or substitute id for predefined packages
671            (*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        // For newly published packages, runtime ID matches storage ID.
679        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        // Here we optimistically push the package that is being published/upgraded
685        // and if there is an error of any kind (verification or module init) we
686        // remove it.
687        // The call to `pop_last_package` later is fine because we cannot re-enter and
688        // the last package we pushed is the one we are verifying and running the init from
689        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            // no upgrade cap for genesis modules
702            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                /* has_public_transfer */ true,
708                /* used_in_non_entry_move_call */ false,
709                &bcs::to_bytes(cap).unwrap(),
710            )?)]
711        };
712        Ok(values)
713    }
714
715    /// Upgrade a Move package.  Returns an `UpgradeReceipt` for the upgraded package on success.
716    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        // Make sure the passed-in package ID matches the package ID in the `upgrade_ticket`.
756        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        // Check digest.
768        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        // Check that this package ID points to a package and get the package we're upgrading.
783        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        // Upgraded packages share their predecessor's runtime ID but get a new storage ID.
790        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            // find newly added modules to the package,
813            // and error if they have init functions
814            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(&current_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                // TODO we cannot run 'init' on upgrade yet due to global type cache limitations
845                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        // Make sure this is a known upgrade policy.
870        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, /* include code */ 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            /* include code */ 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        // If we disallow new modules double check that there are no modules left in `new_normalized`.
922        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    /***************************************************************************************************
997     * Move execution
998     **************************************************************************************************/
999
1000    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        // script visibility checked manually for entry points
1016        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        // When this function is used during publishing, it
1027        // may be executed several times, with objects being
1028        // created in the Move VM in each Move call. In such
1029        // case, we need to update TxContext value so that it
1030        // reflects what happened each time we call into the
1031        // Move VM (e.g. to account for the number of created
1032        // objects).
1033        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        // TODO(https://github.com/MystenLabs/sui/issues/69): avoid this redundant serialization by exposing VM API that allows us to run the linker directly on `Vec<CompiledModule>`
1053        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        // run the Sui verifier
1072        for module in modules {
1073            // Run Sui bytecode verifier, which runs some additional checks that assume the Move
1074            // bytecode verifier has passed.
1075            sui_verifier::verifier::sui_verify_module_unmetered(
1076                module,
1077                &BTreeMap::new(),
1078                &context
1079                    .protocol_config
1080                    .verifier_config(/* signing_limits */ 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                // `init` is currently only called on packages when they are published for the
1110                // first time, meaning their runtime and storage IDs match. If this were to change
1111                // for some reason, then we would need to perform relocation here.
1112                &module_id,
1113                &module_id,
1114                INIT_FN_NAME,
1115                vec![],
1116                vec![],
1117                /* is_init */ 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    /***************************************************************************************************
1133     * Move signatures
1134     **************************************************************************************************/
1135
1136    /// Helper marking what function we are invoking
1137    #[derive(PartialEq, Eq, Clone, Copy)]
1138    enum FunctionKind {
1139        PrivateEntry,
1140        PublicEntry,
1141        NonEntry,
1142        Init,
1143    }
1144
1145    /// Used to remember type information about a type when resolving the signature
1146    enum ValueKind {
1147        Object {
1148            type_: MoveObjectType,
1149            has_public_transfer: bool,
1150        },
1151        Raw(Type, AbilitySet),
1152    }
1153
1154    struct LoadedFunctionInfo {
1155        /// The kind of the function, e.g. public or private or init
1156        kind: FunctionKind,
1157        /// The signature information of the function
1158        signature: LoadedFunctionInstantiation,
1159        /// Object or type information for the return values
1160        return_value_kinds: Vec<ValueKind>,
1161        /// Definition index of the function
1162        index: FunctionDefinitionIndex,
1163        /// The length of the function used for setting error information, or 0 if native
1164        last_instr: CodeOffset,
1165    }
1166
1167    /// Checks that the function to be called is either
1168    /// - an entry function
1169    /// - a public function that does not return references
1170    /// - module init (only internal usage)
1171    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        // entry on init is now banned, so ban invoking it
1210        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    /// substitutes the type arguments into the parameter and return types
1277    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    /// Checks that the non-entry function does not return references. And marks the return values
1306    /// as object or non-object return values
1307    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                    // for dev-inspect, just dereference the value
1320                    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        // If we find an internal type parameter, the call is automatically invalid--since we
1413        // are not in a module and cannot define any types to satisfy the internal constraint.
1414        let Some((internal_idx, _)) = internal_type_parameters
1415            .iter()
1416            .enumerate()
1417            .find(|(_, is_internal)| **is_internal)
1418        else {
1419            // No `internal` type parameters, so it is ok to call
1420            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        /* mut ref */
1443        Vec<(LocalIndex, ValueKind)>,
1444        Vec<Vec<u8>>,
1445    );
1446
1447    /// Serializes the arguments into BCS values for Move. Performs the necessary type checking for
1448    /// each value
1449    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        // check the arity
1458        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        // an init function can have one or two arguments, with the last one always being of type
1464        // &mut TxContext and the additional (first) one representing a one time witness type (see
1465        // one_time_witness verifier pass for additional explanation)
1466        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        // check the types and remember which are by mutable ref
1483        let mut by_mut_ref = vec![];
1484        let mut serialized_args = Vec::with_capacity(num_args);
1485        // an init function can have one or two arguments, with the last one always being of type
1486        // &mut TxContext and the additional (first) one representing a one time witness type (see
1487        // one_time_witness verifier pass for additional explanation)
1488        if has_one_time_witness {
1489            // one time witness type is a struct with a single bool filed which in bcs is encoded as
1490            // 0x01
1491            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    /// checks that the value is compatible with the specified type
1552    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            // For dev-spect, allow any BCS bytes. This does mean internal invariants for types can
1560            // be violated (like for string or Option)
1561            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            // Any means this was just some bytes passed in as an argument (as opposed to being
1570            // generated from a Move function). Meaning we only allow "primitive" values
1571            // and might need to run validation in addition to the BCS layout
1572            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 the type has been fixed, make sure the types match up
1613                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                // Now make sure the param type is a struct instantiation of the receiving struct
1623                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            // SAFETY: Preserving existing behaviour for identifier deserialization.
1659            Ok(unsafe { Identifier::new_unchecked(ident) })
1660        }
1661    }
1662
1663    // Convert a type input which may refer to a type by multiple different IDs and convert it to a
1664    // TypeTag that only uses defining IDs.
1665    //
1666    // It's suboptimal to traverse the type, load, and then go back to a typetag to resolve to
1667    // defining IDs in the typetag, but it's the cleanest solution ATM without adding in additional
1668    // machinery. With the new linkage resolution that we will be adding this will
1669    // be much cleaner however, we'll hold off on adding that in here, and instead add it in the
1670    // new execution code.
1671    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                // SAFETY: Preserving existing behaviour for identifier deserialization within type
1761                // tags and inputs.
1762                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    // Returns Some(kind) if the type is a reference to the TxnContext. kind being Mutable with
1799    // a MutableReference, and Immutable otherwise.
1800    // Returns None for all other types
1801    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    /// Returns Some(layout) iff it is a primitive, an ID, a String, or an option/vector of a valid type
1832    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                // is option of a string
1861                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    // We use a `OnceCell` for two reasons. One to cache the ability set for the type so that it
1887    // is not recomputed for each element of the vector. And two, to avoid computing the abilities
1888    // in the case where `max_ptb_value_size_v2` is false--this removes any case of diverging
1889    // based on the result of `get_type_abilities`.
1890    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        // Do not cap size for epoch change/genesis
1913        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            // No primitive type layout was able to be determined for the type. Assume the worst
1934            // and the value is of maximal depth.
1935            None => context.protocol_config.max_move_value_depth(),
1936            Some(layout) => amplification(&layout)?,
1937        };
1938
1939        // Computed amplification should never be zero
1940        debug_assert!(amplification != 0);
1941        // We assume here that any value that can be created must be bounded by the max move value
1942        // depth so assert that this invariant holds.
1943        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    /***************************************************************************************************
1955     * Special serialization formats
1956     **************************************************************************************************/
1957
1958    /// Special enum for values that need additional validation, in other words
1959    /// There is validation to do on top of the BCS layout. Currently only needed for
1960    /// strings
1961    #[derive(Debug)]
1962    pub enum PrimitiveArgumentLayout {
1963        /// An option
1964        Option(Box<PrimitiveArgumentLayout>),
1965        /// A vector
1966        Vector(Box<PrimitiveArgumentLayout>),
1967        /// An ASCII encoded string
1968        Ascii,
1969        /// A UTF8 encoded string
1970        UTF8,
1971        // needed for Option validation
1972        Bool,
1973        U8,
1974        U16,
1975        U32,
1976        U64,
1977        U128,
1978        U256,
1979        Address,
1980    }
1981
1982    impl PrimitiveArgumentLayout {
1983        /// returns true iff all BCS compatible bytes are actually values for this type.
1984        /// For example, this function returns false for Option and Strings since they need additional
1985        /// validation.
1986        pub fn bcs_only(&self) -> bool {
1987            match self {
1988                // have additional restrictions past BCS
1989                PrimitiveArgumentLayout::Option(_)
1990                | PrimitiveArgumentLayout::Ascii
1991                | PrimitiveArgumentLayout::UTF8 => false,
1992                // Move primitives are BCS compatible and do not need additional validation
1993                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                // vector only needs validation if it's inner type does
2002                PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
2003            }
2004        }
2005    }
2006
2007    /// Checks the bytes against the `SpecialArgumentLayout` using `bcs`. It does not actually generate
2008    /// the deserialized value, only walks the bytes. While not necessary if the layout does not contain
2009    /// special arguments (e.g. Option or String) we check the BCS bytes for predictability
2010    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                // primitive move value cases, which are hit to make sure the correct number of bytes
2053                // are removed for elements of an option/vector
2054                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}