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