sui_adapter_v1/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::execution_mode::ExecutionMode;
9    use crate::execution_value::{
10        CommandKind, ExecutionState, ObjectContents, ObjectValue, RawValueType, Value,
11    };
12    use crate::gas_charger::GasCharger;
13    use move_binary_format::{
14        compatibility::{Compatibility, InclusionCheck},
15        errors::{Location, PartialVMResult, VMResult},
16        file_format::{AbilitySet, CodeOffset, FunctionDefinitionIndex, LocalIndex, Visibility},
17        file_format_common::VERSION_6,
18        normalized, CompiledModule,
19    };
20    use move_core_types::{
21        account_address::AccountAddress,
22        identifier::{IdentStr, Identifier},
23        language_storage::{ModuleId, TypeTag},
24        u256::U256,
25    };
26    use move_vm_runtime::{
27        move_vm::MoveVM,
28        session::{LoadedFunctionInstantiation, SerializedReturnValues},
29    };
30    use move_vm_types::loaded_data::runtime_types::{CachedDatatype, Type};
31    use serde::{de::DeserializeSeed, Deserialize};
32    use std::{
33        collections::{BTreeMap, BTreeSet},
34        fmt,
35        sync::Arc,
36    };
37    use sui_move_natives::object_runtime::ObjectRuntime;
38    use sui_protocol_config::ProtocolConfig;
39    use sui_types::execution_status::{CommandArgumentError, PackageUpgradeError};
40    use sui_types::storage::{get_package_objects, PackageObject};
41    use sui_types::{
42        base_types::{
43            MoveLegacyTxContext, MoveObjectType, ObjectID, SuiAddress, TxContext, TxContextKind,
44            RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, TX_CONTEXT_MODULE_NAME,
45            TX_CONTEXT_STRUCT_NAME,
46        },
47        coin::Coin,
48        error::{command_argument_error, ExecutionError, ExecutionErrorKind},
49        id::RESOLVED_SUI_ID,
50        metrics::LimitsMetrics,
51        move_package::{
52            normalize_deserialized_modules, MovePackage, UpgradeCap, UpgradePolicy, UpgradeReceipt,
53            UpgradeTicket,
54        },
55        transaction::{Argument, Command, ProgrammableMoveCall, ProgrammableTransaction},
56        transfer::RESOLVED_RECEIVING_STRUCT,
57        SUI_FRAMEWORK_ADDRESS,
58    };
59    use sui_verifier::{
60        private_generics::{EVENT_MODULE, PRIVATE_TRANSFER_FUNCTIONS, TRANSFER_MODULE},
61        INIT_FN_NAME,
62    };
63    use tracing::instrument;
64
65    use crate::adapter::substitute_package_id;
66    use crate::programmable_transactions::context::*;
67
68    pub fn execute<Mode: ExecutionMode>(
69        protocol_config: &ProtocolConfig,
70        metrics: Arc<LimitsMetrics>,
71        vm: &MoveVM,
72        state_view: &mut dyn ExecutionState,
73        tx_context: &mut TxContext,
74        gas_charger: &mut GasCharger,
75        pt: ProgrammableTransaction,
76    ) -> Result<Mode::ExecutionResults, ExecutionError> {
77        let ProgrammableTransaction { inputs, commands } = pt;
78        let mut context = ExecutionContext::new(
79            protocol_config,
80            metrics,
81            vm,
82            state_view,
83            tx_context,
84            gas_charger,
85            inputs,
86        )?;
87        // execute commands
88        let mut mode_results = Mode::empty_results();
89        for (idx, command) in commands.into_iter().enumerate() {
90            if let Err(err) = execute_command::<Mode>(&mut context, &mut mode_results, command) {
91                let object_runtime: &ObjectRuntime = context.object_runtime();
92                // We still need to record the loaded child objects for replay
93                let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
94                drop(context);
95                state_view.save_loaded_runtime_objects(loaded_runtime_objects);
96                return Err(err.with_command_index(idx));
97            };
98        }
99
100        // Save loaded objects table in case we fail in post execution
101        let object_runtime: &ObjectRuntime = context.object_runtime();
102        // We still need to record the loaded child objects for replay
103        // Record the objects loaded at runtime (dynamic fields + received) for
104        // storage rebate calculation.
105        let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
106
107        // apply changes
108        let finished = context.finish::<Mode>();
109        // Save loaded objects for debug. We dont want to lose the info
110        state_view.save_loaded_runtime_objects(loaded_runtime_objects);
111        state_view.record_execution_results(finished?)?;
112        Ok(mode_results)
113    }
114
115    /// Execute a single command
116    #[instrument(level = "trace", skip_all)]
117    fn execute_command<Mode: ExecutionMode>(
118        context: &mut ExecutionContext<'_, '_, '_>,
119        mode_results: &mut Mode::ExecutionResults,
120        command: Command,
121    ) -> Result<(), ExecutionError> {
122        let mut argument_updates = Mode::empty_arguments();
123        let results = match command {
124            Command::MakeMoveVec(tag_opt, args) if args.is_empty() => {
125                let Some(tag) = tag_opt else {
126                    invariant_violation!(
127                        "input checker ensures if args are empty, there is a type specified"
128                    );
129                };
130                // SAFETY: Preserving existing behaviour for identifier deserialization within type
131                // tags and inputs.
132                let tag = unsafe { tag.into_type_tag_unchecked() };
133
134                let elem_ty = context
135                    .load_type(&tag)
136                    .map_err(|e| context.convert_vm_error(e))?;
137                let ty = Type::Vector(Box::new(elem_ty));
138                let abilities = context
139                    .vm
140                    .get_runtime()
141                    .get_type_abilities(&ty)
142                    .map_err(|e| context.convert_vm_error(e))?;
143                // BCS layout for any empty vector should be the same
144                let bytes = bcs::to_bytes::<Vec<u8>>(&vec![]).unwrap();
145                vec![Value::Raw(
146                    RawValueType::Loaded {
147                        ty,
148                        abilities,
149                        used_in_non_entry_move_call: false,
150                    },
151                    bytes,
152                )]
153            }
154            Command::MakeMoveVec(tag_opt, args) => {
155                let mut res = vec![];
156                leb128::write::unsigned(&mut res, args.len() as u64).unwrap();
157                let mut arg_iter = args.into_iter().enumerate();
158                let (mut used_in_non_entry_move_call, elem_ty) = match tag_opt {
159                    Some(tag) => {
160                        // SAFETY: Preserving existing behaviour for identifier deserialization within type
161                        // tags and inputs.
162                        let tag = unsafe { tag.into_type_tag_unchecked() };
163
164                        let elem_ty = context
165                            .load_type(&tag)
166                            .map_err(|e| context.convert_vm_error(e))?;
167                        (false, elem_ty)
168                    }
169                    // If no tag specified, it _must_ be an object
170                    None => {
171                        // empty args covered above
172                        let (idx, arg) = arg_iter.next().unwrap();
173                        let obj: ObjectValue =
174                            context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
175                        obj.write_bcs_bytes(&mut res);
176                        (obj.used_in_non_entry_move_call, obj.type_)
177                    }
178                };
179                for (idx, arg) in arg_iter {
180                    let value: Value = context.by_value_arg(CommandKind::MakeMoveVec, idx, arg)?;
181                    check_param_type::<Mode>(context, idx, &value, &elem_ty)?;
182                    used_in_non_entry_move_call =
183                        used_in_non_entry_move_call || value.was_used_in_non_entry_move_call();
184                    value.write_bcs_bytes(&mut res);
185                }
186                let ty = Type::Vector(Box::new(elem_ty));
187                let abilities = context
188                    .vm
189                    .get_runtime()
190                    .get_type_abilities(&ty)
191                    .map_err(|e| context.convert_vm_error(e))?;
192                vec![Value::Raw(
193                    RawValueType::Loaded {
194                        ty,
195                        abilities,
196                        used_in_non_entry_move_call,
197                    },
198                    res,
199                )]
200            }
201            Command::TransferObjects(objs, addr_arg) => {
202                let objs: Vec<ObjectValue> = objs
203                    .into_iter()
204                    .enumerate()
205                    .map(|(idx, arg)| context.by_value_arg(CommandKind::TransferObjects, idx, arg))
206                    .collect::<Result<_, _>>()?;
207                let addr: SuiAddress =
208                    context.by_value_arg(CommandKind::TransferObjects, objs.len(), addr_arg)?;
209                for obj in objs {
210                    obj.ensure_public_transfer_eligible()?;
211                    context.transfer_object(obj, addr)?;
212                }
213                vec![]
214            }
215            Command::SplitCoins(coin_arg, amount_args) => {
216                let mut obj: ObjectValue = context.borrow_arg_mut(0, coin_arg)?;
217                let ObjectContents::Coin(coin) = &mut obj.contents else {
218                    let e = ExecutionErrorKind::command_argument_error(
219                        CommandArgumentError::TypeMismatch,
220                        0,
221                    );
222                    let msg = "Expected a coin but got an non coin object".to_owned();
223                    return Err(ExecutionError::new_with_source(e, msg));
224                };
225                let split_coins = amount_args
226                    .into_iter()
227                    .map(|amount_arg| {
228                        let amount: u64 =
229                            context.by_value_arg(CommandKind::SplitCoins, 1, amount_arg)?;
230                        let new_coin_id = context.fresh_id()?;
231                        let new_coin = coin.split(amount, new_coin_id)?;
232                        let coin_type = obj.type_.clone();
233                        // safe because we are propagating the coin type, and relying on the internal
234                        // invariant that coin values have a coin type
235                        let new_coin = unsafe { ObjectValue::coin(coin_type, new_coin) };
236                        Ok(Value::Object(new_coin))
237                    })
238                    .collect::<Result<_, ExecutionError>>()?;
239                context.restore_arg::<Mode>(&mut argument_updates, coin_arg, Value::Object(obj))?;
240                split_coins
241            }
242            Command::MergeCoins(target_arg, coin_args) => {
243                let mut target: ObjectValue = context.borrow_arg_mut(0, target_arg)?;
244                let ObjectContents::Coin(target_coin) = &mut target.contents else {
245                    let e = ExecutionErrorKind::command_argument_error(
246                        CommandArgumentError::TypeMismatch,
247                        0,
248                    );
249                    let msg = "Expected a coin but got an non coin object".to_owned();
250                    return Err(ExecutionError::new_with_source(e, msg));
251                };
252                let coins: Vec<ObjectValue> = coin_args
253                    .into_iter()
254                    .enumerate()
255                    .map(|(idx, arg)| context.by_value_arg(CommandKind::MergeCoins, idx + 1, arg))
256                    .collect::<Result<_, _>>()?;
257                for (idx, coin) in coins.into_iter().enumerate() {
258                    if target.type_ != coin.type_ {
259                        let e = ExecutionErrorKind::command_argument_error(
260                            CommandArgumentError::TypeMismatch,
261                            (idx + 1) as u16,
262                        );
263                        let msg = "Coins do not have the same type".to_owned();
264                        return Err(ExecutionError::new_with_source(e, msg));
265                    }
266                    let ObjectContents::Coin(Coin { id, balance }) = coin.contents else {
267                        invariant_violation!(
268                            "Target coin was a coin, and we already checked for the same type. \
269                            This should be a coin"
270                        );
271                    };
272                    context.delete_id(*id.object_id())?;
273                    target_coin.add(balance)?;
274                }
275                context.restore_arg::<Mode>(
276                    &mut argument_updates,
277                    target_arg,
278                    Value::Object(target),
279                )?;
280                vec![]
281            }
282            Command::MoveCall(move_call) => {
283                let ProgrammableMoveCall {
284                    package,
285                    module,
286                    function,
287                    type_arguments,
288                    arguments,
289                } = *move_call;
290
291                // SAFETY: Preserving existing behaviour for identifier deserialization.
292                let module = unsafe { Identifier::new_unchecked(module) };
293                let function = unsafe { Identifier::new_unchecked(function) };
294
295                // Convert type arguments to `Type`s
296                let mut loaded_type_arguments = Vec::with_capacity(type_arguments.len());
297                for (ix, type_arg) in type_arguments.into_iter().enumerate() {
298                    // SAFETY: Preserving existing behaviour for identifier deserialization within type
299                    // tags and inputs.
300                    let type_arg = unsafe { type_arg.into_type_tag_unchecked() };
301
302                    let ty = context
303                        .load_type(&type_arg)
304                        .map_err(|e| context.convert_type_argument_error(ix, e))?;
305                    loaded_type_arguments.push(ty);
306                }
307
308                let original_address = context.set_link_context(package)?;
309                let runtime_id = ModuleId::new(original_address, module);
310                let return_values = execute_move_call::<Mode>(
311                    context,
312                    &mut argument_updates,
313                    &runtime_id,
314                    &function,
315                    loaded_type_arguments,
316                    arguments,
317                    /* is_init */ false,
318                );
319
320                context.linkage_view.reset_linkage();
321                return_values?
322            }
323            Command::Publish(modules, dep_ids) => {
324                execute_move_publish::<Mode>(context, &mut argument_updates, modules, dep_ids)?
325            }
326            Command::Upgrade(modules, dep_ids, current_package_id, upgrade_ticket) => {
327                execute_move_upgrade::<Mode>(
328                    context,
329                    modules,
330                    dep_ids,
331                    current_package_id,
332                    upgrade_ticket,
333                )?
334            }
335        };
336
337        Mode::finish_command(context, mode_results, argument_updates, &results)?;
338        context.push_command_results(results)?;
339        Ok(())
340    }
341
342    /// Execute a single Move call
343    fn execute_move_call<Mode: ExecutionMode>(
344        context: &mut ExecutionContext<'_, '_, '_>,
345        argument_updates: &mut Mode::ArgumentUpdates,
346        module_id: &ModuleId,
347        function: &IdentStr,
348        type_arguments: Vec<Type>,
349        arguments: Vec<Argument>,
350        is_init: bool,
351    ) -> Result<Vec<Value>, ExecutionError> {
352        // check that the function is either an entry function or a valid public function
353        let LoadedFunctionInfo {
354            kind,
355            signature,
356            return_value_kinds,
357            index,
358            last_instr,
359        } = check_visibility_and_signature::<Mode>(
360            context,
361            module_id,
362            function,
363            &type_arguments,
364            is_init,
365        )?;
366        // build the arguments, storing meta data about by-mut-ref args
367        let (tx_context_kind, by_mut_ref, serialized_arguments) =
368            build_move_args::<Mode>(context, module_id, function, kind, &signature, &arguments)?;
369        // invoke the VM
370        let SerializedReturnValues {
371            mutable_reference_outputs,
372            return_values,
373        } = vm_move_call(
374            context,
375            module_id,
376            function,
377            type_arguments,
378            tx_context_kind,
379            serialized_arguments,
380        )?;
381        assert_invariant!(
382            by_mut_ref.len() == mutable_reference_outputs.len(),
383            "lost mutable input"
384        );
385
386        context.take_user_events(module_id, index, last_instr)?;
387
388        // save the link context because calls to `make_value` below can set new ones, and we don't want
389        // it to be clobbered.
390        let saved_linkage = context.linkage_view.steal_linkage();
391        // write back mutable inputs. We also update if they were used in non entry Move calls
392        // though we do not care for immutable usages of objects or other values
393        let used_in_non_entry_move_call = kind == FunctionKind::NonEntry;
394        let res = write_back_results::<Mode>(
395            context,
396            argument_updates,
397            &arguments,
398            used_in_non_entry_move_call,
399            mutable_reference_outputs
400                .into_iter()
401                .map(|(i, bytes, _layout)| (i, bytes)),
402            by_mut_ref,
403            return_values.into_iter().map(|(bytes, _layout)| bytes),
404            return_value_kinds,
405        );
406
407        context.linkage_view.restore_linkage(saved_linkage)?;
408        res
409    }
410
411    fn write_back_results<Mode: ExecutionMode>(
412        context: &mut ExecutionContext<'_, '_, '_>,
413        argument_updates: &mut Mode::ArgumentUpdates,
414        arguments: &[Argument],
415        non_entry_move_call: bool,
416        mut_ref_values: impl IntoIterator<Item = (u8, Vec<u8>)>,
417        mut_ref_kinds: impl IntoIterator<Item = (u8, ValueKind)>,
418        return_values: impl IntoIterator<Item = Vec<u8>>,
419        return_value_kinds: impl IntoIterator<Item = ValueKind>,
420    ) -> Result<Vec<Value>, ExecutionError> {
421        for ((i, bytes), (j, kind)) in mut_ref_values.into_iter().zip(mut_ref_kinds) {
422            assert_invariant!(i == j, "lost mutable input");
423            let arg_idx = i as usize;
424            let value = make_value(context, kind, bytes, non_entry_move_call)?;
425            context.restore_arg::<Mode>(argument_updates, arguments[arg_idx], value)?;
426        }
427
428        return_values
429            .into_iter()
430            .zip(return_value_kinds)
431            .map(|(bytes, kind)| {
432                // only non entry functions have return values
433                make_value(
434                    context, kind, bytes, /* used_in_non_entry_move_call */ true,
435                )
436            })
437            .collect()
438    }
439
440    fn make_value(
441        context: &mut ExecutionContext<'_, '_, '_>,
442        value_info: ValueKind,
443        bytes: Vec<u8>,
444        used_in_non_entry_move_call: bool,
445    ) -> Result<Value, ExecutionError> {
446        Ok(match value_info {
447            ValueKind::Object {
448                type_,
449                has_public_transfer,
450            } => Value::Object(context.make_object_value(
451                type_,
452                has_public_transfer,
453                used_in_non_entry_move_call,
454                &bytes,
455            )?),
456            ValueKind::Raw(ty, abilities) => Value::Raw(
457                RawValueType::Loaded {
458                    ty,
459                    abilities,
460                    used_in_non_entry_move_call,
461                },
462                bytes,
463            ),
464        })
465    }
466
467    /// Publish Move modules and call the init functions.  Returns an `UpgradeCap` for the newly
468    /// published package on success.
469    fn execute_move_publish<Mode: ExecutionMode>(
470        context: &mut ExecutionContext<'_, '_, '_>,
471        argument_updates: &mut Mode::ArgumentUpdates,
472        module_bytes: Vec<Vec<u8>>,
473        dep_ids: Vec<ObjectID>,
474    ) -> Result<Vec<Value>, ExecutionError> {
475        assert_invariant!(
476            !module_bytes.is_empty(),
477            "empty package is checked in transaction input checker"
478        );
479        context
480            .gas_charger
481            .charge_publish_package(module_bytes.iter().map(|v| v.len()).sum())?;
482
483        let mut modules = deserialize_modules::<Mode>(context, &module_bytes)?;
484
485        // It should be fine that this does not go through ExecutionContext::fresh_id since the Move
486        // runtime does not to know about new packages created, since Move objects and Move packages
487        // cannot interact
488        let runtime_id = if Mode::packages_are_predefined() {
489            // do not calculate or substitute id for predefined packages
490            (*modules[0].self_id().address()).into()
491        } else {
492            let id = context.tx_context.fresh_id();
493            substitute_package_id(&mut modules, id)?;
494            id
495        };
496
497        // For newly published packages, runtime ID matches storage ID.
498        let storage_id = runtime_id;
499        let dependencies = fetch_packages(context, &dep_ids)?;
500        let package =
501            context.new_package(&modules, dependencies.iter().map(|p| p.move_package()))?;
502
503        // Here we optimistically push the package that is being published/upgraded
504        // and if there is an error of any kind (verification or module init) we
505        // remove it.
506        // The call to `pop_last_package` later is fine because we cannot re-enter and
507        // the last package we pushed is the one we are verifying and running the init from
508        context.linkage_view.set_linkage(&package)?;
509        context.write_package(package);
510        let res = publish_and_verify_modules(context, runtime_id, &modules)
511            .and_then(|_| init_modules::<Mode>(context, argument_updates, &modules));
512        context.linkage_view.reset_linkage();
513        if res.is_err() {
514            context.pop_package();
515        }
516        res?;
517
518        let values = if Mode::packages_are_predefined() {
519            // no upgrade cap for genesis modules
520            vec![]
521        } else {
522            let cap = &UpgradeCap::new(context.fresh_id()?, storage_id);
523            vec![Value::Object(context.make_object_value(
524                UpgradeCap::type_().into(),
525                /* has_public_transfer */ true,
526                /* used_in_non_entry_move_call */ false,
527                &bcs::to_bytes(cap).unwrap(),
528            )?)]
529        };
530        Ok(values)
531    }
532
533    /// Upgrade a Move package.  Returns an `UpgradeReceipt` for the upgraded package on success.
534    fn execute_move_upgrade<Mode: ExecutionMode>(
535        context: &mut ExecutionContext<'_, '_, '_>,
536        module_bytes: Vec<Vec<u8>>,
537        dep_ids: Vec<ObjectID>,
538        current_package_id: ObjectID,
539        upgrade_ticket_arg: Argument,
540    ) -> Result<Vec<Value>, ExecutionError> {
541        assert_invariant!(
542            !module_bytes.is_empty(),
543            "empty package is checked in transaction input checker"
544        );
545        context
546            .gas_charger
547            .charge_upgrade_package(module_bytes.iter().map(|v| v.len()).sum())?;
548
549        let upgrade_ticket_type = context
550            .load_type_from_struct(&UpgradeTicket::type_())
551            .map_err(|e| context.convert_vm_error(e))?;
552        let upgrade_receipt_type = context
553            .load_type_from_struct(&UpgradeReceipt::type_())
554            .map_err(|e| context.convert_vm_error(e))?;
555
556        let upgrade_ticket: UpgradeTicket = {
557            let mut ticket_bytes = Vec::new();
558            let ticket_val: Value =
559                context.by_value_arg(CommandKind::Upgrade, 0, upgrade_ticket_arg)?;
560            check_param_type::<Mode>(context, 0, &ticket_val, &upgrade_ticket_type)?;
561            ticket_val.write_bcs_bytes(&mut ticket_bytes);
562            bcs::from_bytes(&ticket_bytes).map_err(|_| {
563                ExecutionError::from_kind(ExecutionErrorKind::CommandArgumentError {
564                    arg_idx: 0,
565                    kind: CommandArgumentError::InvalidBCSBytes,
566                })
567            })?
568        };
569
570        // Make sure the passed-in package ID matches the package ID in the `upgrade_ticket`.
571        if current_package_id != upgrade_ticket.package.bytes {
572            return Err(ExecutionError::from_kind(
573                ExecutionErrorKind::PackageUpgradeError {
574                    upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
575                        package_id: current_package_id,
576                        ticket_id: upgrade_ticket.package.bytes,
577                    },
578                },
579            ));
580        }
581
582        // Check digest.
583        let hash_modules = true;
584        let computed_digest =
585            MovePackage::compute_digest_for_modules_and_deps(&module_bytes, &dep_ids, hash_modules)
586                .to_vec();
587        if computed_digest != upgrade_ticket.digest {
588            return Err(ExecutionError::from_kind(
589                ExecutionErrorKind::PackageUpgradeError {
590                    upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
591                        digest: computed_digest,
592                    },
593                },
594            ));
595        }
596
597        // Check that this package ID points to a package and get the package we're upgrading.
598        let current_package = fetch_package(context, &upgrade_ticket.package.bytes)?;
599
600        let mut modules = deserialize_modules::<Mode>(context, &module_bytes)?;
601        let runtime_id = current_package.move_package().original_package_id();
602        substitute_package_id(&mut modules, runtime_id)?;
603
604        // Upgraded packages share their predecessor's runtime ID but get a new storage ID.
605        let storage_id = context.tx_context.fresh_id();
606
607        let dependencies = fetch_packages(context, &dep_ids)?;
608        let package = context.upgrade_package(
609            storage_id,
610            current_package.move_package(),
611            &modules,
612            dependencies.iter().map(|p| p.move_package()),
613        )?;
614
615        context.linkage_view.set_linkage(&package)?;
616        let res = publish_and_verify_modules(context, runtime_id, &modules);
617        context.linkage_view.reset_linkage();
618        res?;
619
620        check_compatibility(
621            context,
622            current_package.move_package(),
623            &modules,
624            upgrade_ticket.policy,
625        )?;
626
627        context.write_package(package);
628        Ok(vec![Value::Raw(
629            RawValueType::Loaded {
630                ty: upgrade_receipt_type,
631                abilities: AbilitySet::EMPTY,
632                used_in_non_entry_move_call: false,
633            },
634            bcs::to_bytes(&UpgradeReceipt::new(upgrade_ticket, storage_id)).unwrap(),
635        )])
636    }
637
638    fn check_compatibility<'a>(
639        context: &ExecutionContext,
640        existing_package: &MovePackage,
641        upgrading_modules: impl IntoIterator<Item = &'a CompiledModule>,
642        policy: u8,
643    ) -> Result<(), ExecutionError> {
644        // Make sure this is a known upgrade policy.
645        let Ok(policy) = UpgradePolicy::try_from(policy) else {
646            return Err(ExecutionError::from_kind(
647                ExecutionErrorKind::PackageUpgradeError {
648                    upgrade_error: PackageUpgradeError::UnknownUpgradePolicy { policy },
649                },
650            ));
651        };
652
653        let pool = &mut normalized::RcPool::new();
654        let binary_config = context.protocol_config.binary_config(None);
655        let Ok(current_normalized) =
656            existing_package.normalize(pool, &binary_config, /* include code */ true)
657        else {
658            invariant_violation!("Tried to normalize modules in existing package but failed")
659        };
660
661        let mut new_normalized = normalize_deserialized_modules(
662            pool,
663            upgrading_modules.into_iter(),
664            /* include code */ true,
665        );
666        for (name, cur_module) in current_normalized {
667            let Some(new_module) = new_normalized.remove(&name) else {
668                return Err(ExecutionError::new_with_source(
669                    ExecutionErrorKind::PackageUpgradeError {
670                        upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
671                    },
672                    format!("Existing module {name} not found in next version of package"),
673                ));
674            };
675
676            check_module_compatibility(&policy, &cur_module, &new_module)?;
677        }
678
679        Ok(())
680    }
681
682    fn check_module_compatibility(
683        policy: &UpgradePolicy,
684        cur_module: &move_binary_format::compatibility::Module,
685        new_module: &move_binary_format::compatibility::Module,
686    ) -> Result<(), ExecutionError> {
687        match policy {
688            UpgradePolicy::Additive => InclusionCheck::Subset.check(cur_module, new_module),
689            UpgradePolicy::DepOnly => InclusionCheck::Equal.check(cur_module, new_module),
690            UpgradePolicy::Compatible => {
691                let compatibility = Compatibility::upgrade_check();
692
693                compatibility.check(cur_module, new_module)
694            }
695        }
696        .map_err(|e| {
697            ExecutionError::new_with_source(
698                ExecutionErrorKind::PackageUpgradeError {
699                    upgrade_error: PackageUpgradeError::IncompatibleUpgrade,
700                },
701                e,
702            )
703        })
704    }
705
706    fn fetch_package(
707        context: &ExecutionContext<'_, '_, '_>,
708        package_id: &ObjectID,
709    ) -> Result<PackageObject, ExecutionError> {
710        let mut fetched_packages = fetch_packages(context, vec![package_id])?;
711        assert_invariant!(
712            fetched_packages.len() == 1,
713            "Number of fetched packages must match the number of package object IDs if successful."
714        );
715        match fetched_packages.pop() {
716            Some(pkg) => Ok(pkg),
717            None => invariant_violation!(
718                "We should always fetch a package for each object or return a dependency error."
719            ),
720        }
721    }
722
723    fn fetch_packages<'ctx, 'vm, 'state, 'a>(
724        context: &'ctx ExecutionContext<'vm, 'state, 'a>,
725        package_ids: impl IntoIterator<Item = &'ctx ObjectID>,
726    ) -> Result<Vec<PackageObject>, ExecutionError> {
727        let package_ids: BTreeSet<_> = package_ids.into_iter().collect();
728        match get_package_objects(&context.state_view, package_ids) {
729            Err(e) => Err(ExecutionError::new_with_source(
730                ExecutionErrorKind::PublishUpgradeMissingDependency,
731                e,
732            )),
733            Ok(Err(missing_deps)) => {
734                let msg = format!(
735                    "Missing dependencies: {}",
736                    missing_deps
737                        .into_iter()
738                        .map(|dep| format!("{}", dep))
739                        .collect::<Vec<_>>()
740                        .join(", ")
741                );
742                Err(ExecutionError::new_with_source(
743                    ExecutionErrorKind::PublishUpgradeMissingDependency,
744                    msg,
745                ))
746            }
747            Ok(Ok(pkgs)) => Ok(pkgs),
748        }
749    }
750
751    /***************************************************************************************************
752     * Move execution
753     **************************************************************************************************/
754
755    fn vm_move_call(
756        context: &mut ExecutionContext<'_, '_, '_>,
757        module_id: &ModuleId,
758        function: &IdentStr,
759        type_arguments: Vec<Type>,
760        tx_context_kind: TxContextKind,
761        mut serialized_arguments: Vec<Vec<u8>>,
762    ) -> Result<SerializedReturnValues, ExecutionError> {
763        match tx_context_kind {
764            TxContextKind::None => (),
765            TxContextKind::Mutable | TxContextKind::Immutable => {
766                serialized_arguments.push(context.tx_context.to_bcs_legacy_context());
767            }
768        }
769        // script visibility checked manually for entry points
770        let mut result = context
771            .execute_function_bypass_visibility(
772                module_id,
773                function,
774                type_arguments,
775                serialized_arguments,
776            )
777            .map_err(|e| context.convert_vm_error(e))?;
778
779        // When this function is used during publishing, it
780        // may be executed several times, with objects being
781        // created in the Move VM in each Move call. In such
782        // case, we need to update TxContext value so that it
783        // reflects what happened each time we call into the
784        // Move VM (e.g. to account for the number of created
785        // objects).
786        if tx_context_kind == TxContextKind::Mutable {
787            let Some((_, ctx_bytes, _)) = result.mutable_reference_outputs.pop() else {
788                invariant_violation!("Missing TxContext in reference outputs");
789            };
790            let updated_ctx: MoveLegacyTxContext = bcs::from_bytes(&ctx_bytes).map_err(|e| {
791                ExecutionError::invariant_violation(format!(
792                    "Unable to deserialize TxContext bytes. {e}"
793                ))
794            })?;
795            context.tx_context.update_state(updated_ctx)?;
796        }
797        Ok(result)
798    }
799
800    #[allow(clippy::extra_unused_type_parameters)]
801    fn deserialize_modules<Mode: ExecutionMode>(
802        context: &mut ExecutionContext<'_, '_, '_>,
803        module_bytes: &[Vec<u8>],
804    ) -> Result<Vec<CompiledModule>, ExecutionError> {
805        let binary_config = context.protocol_config.binary_config(None);
806        let modules = module_bytes
807            .iter()
808            .map(|b| {
809                CompiledModule::deserialize_with_config(b, &binary_config)
810                    .map_err(|e| e.finish(Location::Undefined))
811            })
812            .collect::<VMResult<Vec<CompiledModule>>>()
813            .map_err(|e| context.convert_vm_error(e))?;
814
815        assert_invariant!(
816            !modules.is_empty(),
817            "input checker ensures package is not empty"
818        );
819
820        Ok(modules)
821    }
822
823    fn publish_and_verify_modules(
824        context: &mut ExecutionContext<'_, '_, '_>,
825        package_id: ObjectID,
826        modules: &[CompiledModule],
827    ) -> Result<(), ExecutionError> {
828        // 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>`
829        let new_module_bytes: Vec<_> = modules
830            .iter()
831            .map(|m| {
832                let mut bytes = Vec::new();
833                m.serialize_with_version(VERSION_6, &mut bytes).unwrap();
834                bytes
835            })
836            .collect();
837        context
838            .publish_module_bundle(new_module_bytes, AccountAddress::from(package_id))
839            .map_err(|e| context.convert_vm_error(e))?;
840
841        // run the Sui verifier
842        for module in modules {
843            // Run Sui bytecode verifier, which runs some additional checks that assume the Move
844            // bytecode verifier has passed.
845            sui_verifier::verifier::sui_verify_module_unmetered(module, &BTreeMap::new())?;
846        }
847
848        Ok(())
849    }
850
851    fn init_modules<Mode: ExecutionMode>(
852        context: &mut ExecutionContext<'_, '_, '_>,
853        argument_updates: &mut Mode::ArgumentUpdates,
854        modules: &[CompiledModule],
855    ) -> Result<(), ExecutionError> {
856        let modules_to_init = modules.iter().filter_map(|module| {
857            for fdef in &module.function_defs {
858                let fhandle = module.function_handle_at(fdef.function);
859                let fname = module.identifier_at(fhandle.name);
860                if fname == INIT_FN_NAME {
861                    return Some(module.self_id());
862                }
863            }
864            None
865        });
866
867        for module_id in modules_to_init {
868            let return_values = execute_move_call::<Mode>(
869                context,
870                argument_updates,
871                &module_id,
872                INIT_FN_NAME,
873                vec![],
874                vec![],
875                /* is_init */ true,
876            )?;
877
878            assert_invariant!(
879                return_values.is_empty(),
880                "init should not have return values"
881            )
882        }
883
884        Ok(())
885    }
886
887    /***************************************************************************************************
888     * Move signatures
889     **************************************************************************************************/
890
891    /// Helper marking what function we are invoking
892    #[derive(PartialEq, Eq, Clone, Copy)]
893    enum FunctionKind {
894        PrivateEntry,
895        PublicEntry,
896        NonEntry,
897        Init,
898    }
899
900    /// Used to remember type information about a type when resolving the signature
901    enum ValueKind {
902        Object {
903            type_: MoveObjectType,
904            has_public_transfer: bool,
905        },
906        Raw(Type, AbilitySet),
907    }
908
909    struct LoadedFunctionInfo {
910        /// The kind of the function, e.g. public or private or init
911        kind: FunctionKind,
912        /// The signature information of the function
913        signature: LoadedFunctionInstantiation,
914        /// Object or type information for the return values
915        return_value_kinds: Vec<ValueKind>,
916        /// Definition index of the function
917        index: FunctionDefinitionIndex,
918        /// The length of the function used for setting error information, or 0 if native
919        last_instr: CodeOffset,
920    }
921
922    /// Checks that the function to be called is either
923    /// - an entry function
924    /// - a public function that does not return references
925    /// - module init (only internal usage)
926    fn check_visibility_and_signature<Mode: ExecutionMode>(
927        context: &mut ExecutionContext<'_, '_, '_>,
928        module_id: &ModuleId,
929        function: &IdentStr,
930        type_arguments: &[Type],
931        from_init: bool,
932    ) -> Result<LoadedFunctionInfo, ExecutionError> {
933        if from_init {
934            let result = context.load_function(module_id, function, type_arguments);
935            assert_invariant!(
936                result.is_ok(),
937                "The modules init should be able to be loaded"
938            );
939        }
940        let no_new_packages = vec![];
941        let data_store = SuiDataStore::new(&context.linkage_view, &no_new_packages);
942        let module = context
943            .vm
944            .get_runtime()
945            .load_module(module_id, &data_store)
946            .map_err(|e| context.convert_vm_error(e))?;
947        let Some((index, fdef)) = module
948            .function_defs
949            .iter()
950            .enumerate()
951            .find(|(_index, fdef)| {
952                module.identifier_at(module.function_handle_at(fdef.function).name) == function
953            })
954        else {
955            return Err(ExecutionError::new_with_source(
956                ExecutionErrorKind::FunctionNotFound,
957                format!(
958                    "Could not resolve function '{}' in module {}",
959                    function, &module_id,
960                ),
961            ));
962        };
963
964        // entry on init is now banned, so ban invoking it
965        if !from_init && function == INIT_FN_NAME && context.protocol_config.ban_entry_init() {
966            return Err(ExecutionError::new_with_source(
967                ExecutionErrorKind::NonEntryFunctionInvoked,
968                "Cannot call 'init'",
969            ));
970        }
971
972        let last_instr: CodeOffset = fdef
973            .code
974            .as_ref()
975            .map(|code| code.code.len() - 1)
976            .unwrap_or(0) as CodeOffset;
977        let function_kind = match (fdef.visibility, fdef.is_entry) {
978            (Visibility::Private | Visibility::Friend, true) => FunctionKind::PrivateEntry,
979            (Visibility::Public, true) => FunctionKind::PublicEntry,
980            (Visibility::Public, false) => FunctionKind::NonEntry,
981            (Visibility::Private, false) if from_init => {
982                assert_invariant!(
983                    function == INIT_FN_NAME,
984                    "module init specified non-init function"
985                );
986                FunctionKind::Init
987            }
988            (Visibility::Private | Visibility::Friend, false)
989                if Mode::allow_arbitrary_function_calls() =>
990            {
991                FunctionKind::NonEntry
992            }
993            (Visibility::Private | Visibility::Friend, false) => {
994                return Err(ExecutionError::new_with_source(
995                    ExecutionErrorKind::NonEntryFunctionInvoked,
996                    "Can only call `entry` or `public` functions",
997                ));
998            }
999        };
1000        let signature = context
1001            .load_function(module_id, function, type_arguments)
1002            .map_err(|e| context.convert_vm_error(e))?;
1003        let signature =
1004            subst_signature(signature, type_arguments).map_err(|e| context.convert_vm_error(e))?;
1005        let return_value_kinds = match function_kind {
1006            FunctionKind::Init => {
1007                assert_invariant!(
1008                    signature.return_.is_empty(),
1009                    "init functions must have no return values"
1010                );
1011                vec![]
1012            }
1013            FunctionKind::PrivateEntry | FunctionKind::PublicEntry | FunctionKind::NonEntry => {
1014                check_non_entry_signature::<Mode>(context, module_id, function, &signature)?
1015            }
1016        };
1017        check_private_generics(context, module_id, function, type_arguments)?;
1018        Ok(LoadedFunctionInfo {
1019            kind: function_kind,
1020            signature,
1021            return_value_kinds,
1022            index: FunctionDefinitionIndex(index as u16),
1023            last_instr,
1024        })
1025    }
1026
1027    /// substitutes the type arguments into the parameter and return types
1028    fn subst_signature(
1029        signature: LoadedFunctionInstantiation,
1030        type_arguments: &[Type],
1031    ) -> VMResult<LoadedFunctionInstantiation> {
1032        let LoadedFunctionInstantiation {
1033            parameters,
1034            return_,
1035        } = signature;
1036        let parameters = parameters
1037            .into_iter()
1038            .map(|ty| ty.subst(type_arguments))
1039            .collect::<PartialVMResult<Vec<_>>>()
1040            .map_err(|err| err.finish(Location::Undefined))?;
1041        let return_ = return_
1042            .into_iter()
1043            .map(|ty| ty.subst(type_arguments))
1044            .collect::<PartialVMResult<Vec<_>>>()
1045            .map_err(|err| err.finish(Location::Undefined))?;
1046        Ok(LoadedFunctionInstantiation {
1047            parameters,
1048            return_,
1049        })
1050    }
1051
1052    /// Checks that the non-entry function does not return references. And marks the return values
1053    /// as object or non-object return values
1054    fn check_non_entry_signature<Mode: ExecutionMode>(
1055        context: &mut ExecutionContext<'_, '_, '_>,
1056        _module_id: &ModuleId,
1057        _function: &IdentStr,
1058        signature: &LoadedFunctionInstantiation,
1059    ) -> Result<Vec<ValueKind>, ExecutionError> {
1060        signature
1061            .return_
1062            .iter()
1063            .enumerate()
1064            .map(|(idx, return_type)| {
1065                let return_type = match return_type {
1066                    // for dev-inspect, just dereference the value
1067                    Type::Reference(inner) | Type::MutableReference(inner)
1068                        if Mode::allow_arbitrary_values() =>
1069                    {
1070                        inner
1071                    }
1072                    Type::Reference(_) | Type::MutableReference(_) => {
1073                        return Err(ExecutionError::from_kind(
1074                            ExecutionErrorKind::InvalidPublicFunctionReturnType { idx: idx as u16 },
1075                        ))
1076                    }
1077                    t => t,
1078                };
1079                let abilities = context
1080                    .vm
1081                    .get_runtime()
1082                    .get_type_abilities(return_type)
1083                    .map_err(|e| context.convert_vm_error(e))?;
1084                Ok(match return_type {
1085                    Type::MutableReference(_) | Type::Reference(_) => unreachable!(),
1086                    Type::TyParam(_) => {
1087                        invariant_violation!("TyParam should have been substituted")
1088                    }
1089                    Type::Datatype(_) | Type::DatatypeInstantiation(_) if abilities.has_key() => {
1090                        let type_tag = context
1091                            .vm
1092                            .get_runtime()
1093                            .get_type_tag(return_type)
1094                            .map_err(|e| context.convert_vm_error(e))?;
1095                        let TypeTag::Struct(struct_tag) = type_tag else {
1096                            invariant_violation!("Struct type make a non struct type tag")
1097                        };
1098                        ValueKind::Object {
1099                            type_: MoveObjectType::from(*struct_tag),
1100                            has_public_transfer: abilities.has_store(),
1101                        }
1102                    }
1103                    Type::Datatype(_)
1104                    | Type::DatatypeInstantiation(_)
1105                    | Type::Bool
1106                    | Type::U8
1107                    | Type::U64
1108                    | Type::U128
1109                    | Type::Address
1110                    | Type::Signer
1111                    | Type::Vector(_)
1112                    | Type::U16
1113                    | Type::U32
1114                    | Type::U256 => ValueKind::Raw(return_type.clone(), abilities),
1115                })
1116            })
1117            .collect()
1118    }
1119
1120    fn check_private_generics(
1121        _context: &mut ExecutionContext,
1122        module_id: &ModuleId,
1123        function: &IdentStr,
1124        _type_arguments: &[Type],
1125    ) -> Result<(), ExecutionError> {
1126        let module_ident = (module_id.address(), module_id.name());
1127        if module_ident == (&SUI_FRAMEWORK_ADDRESS, EVENT_MODULE) {
1128            return Err(ExecutionError::new_with_source(
1129                ExecutionErrorKind::NonEntryFunctionInvoked,
1130                format!("Cannot directly call functions in sui::{}", EVENT_MODULE),
1131            ));
1132        }
1133
1134        if module_ident == (&SUI_FRAMEWORK_ADDRESS, TRANSFER_MODULE)
1135            && PRIVATE_TRANSFER_FUNCTIONS.contains(&function)
1136        {
1137            let msg = format!(
1138                "Cannot directly call sui::{m}::{f}. \
1139                Use the public variant instead, sui::{m}::public_{f}",
1140                m = TRANSFER_MODULE,
1141                f = function
1142            );
1143            return Err(ExecutionError::new_with_source(
1144                ExecutionErrorKind::NonEntryFunctionInvoked,
1145                msg,
1146            ));
1147        }
1148
1149        Ok(())
1150    }
1151
1152    type ArgInfo = (
1153        TxContextKind,
1154        /* mut ref */
1155        Vec<(LocalIndex, ValueKind)>,
1156        Vec<Vec<u8>>,
1157    );
1158
1159    /// Serializes the arguments into BCS values for Move. Performs the necessary type checking for
1160    /// each value
1161    fn build_move_args<Mode: ExecutionMode>(
1162        context: &mut ExecutionContext<'_, '_, '_>,
1163        module_id: &ModuleId,
1164        function: &IdentStr,
1165        function_kind: FunctionKind,
1166        signature: &LoadedFunctionInstantiation,
1167        args: &[Argument],
1168    ) -> Result<ArgInfo, ExecutionError> {
1169        // check the arity
1170        let parameters = &signature.parameters;
1171        let tx_ctx_kind = match parameters.last() {
1172            Some(t) => is_tx_context(context, t)?,
1173            None => TxContextKind::None,
1174        };
1175        // an init function can have one or two arguments, with the last one always being of type
1176        // &mut TxContext and the additional (first) one representing a one time witness type (see
1177        // one_time_witness verifier pass for additional explanation)
1178        let has_one_time_witness = function_kind == FunctionKind::Init && parameters.len() == 2;
1179        let has_tx_context = tx_ctx_kind != TxContextKind::None;
1180        let num_args = args.len() + (has_one_time_witness as usize) + (has_tx_context as usize);
1181        if num_args != parameters.len() {
1182            return Err(ExecutionError::new_with_source(
1183                ExecutionErrorKind::ArityMismatch,
1184                format!(
1185                    "Expected {:?} argument{} calling function '{}', but found {:?}",
1186                    parameters.len(),
1187                    if parameters.len() == 1 { "" } else { "s" },
1188                    function,
1189                    num_args
1190                ),
1191            ));
1192        }
1193
1194        // check the types and remember which are by mutable ref
1195        let mut by_mut_ref = vec![];
1196        let mut serialized_args = Vec::with_capacity(num_args);
1197        let command_kind = CommandKind::MoveCall {
1198            package: (*module_id.address()).into(),
1199            module: module_id.name(),
1200            function,
1201        };
1202        // an init function can have one or two arguments, with the last one always being of type
1203        // &mut TxContext and the additional (first) one representing a one time witness type (see
1204        // one_time_witness verifier pass for additional explanation)
1205        if has_one_time_witness {
1206            // one time witness type is a struct with a single bool filed which in bcs is encoded as
1207            // 0x01
1208            let bcs_true_value = bcs::to_bytes(&true).unwrap();
1209            serialized_args.push(bcs_true_value)
1210        }
1211        for ((idx, arg), param_ty) in args.iter().copied().enumerate().zip(parameters) {
1212            let (value, non_ref_param_ty): (Value, &Type) = match param_ty {
1213                Type::MutableReference(inner) => {
1214                    let value = context.borrow_arg_mut(idx, arg)?;
1215                    let object_info = if let Value::Object(ObjectValue {
1216                        type_,
1217                        has_public_transfer,
1218                        ..
1219                    }) = &value
1220                    {
1221                        let type_tag = context
1222                            .vm
1223                            .get_runtime()
1224                            .get_type_tag(type_)
1225                            .map_err(|e| context.convert_vm_error(e))?;
1226                        let TypeTag::Struct(struct_tag) = type_tag else {
1227                            invariant_violation!("Struct type make a non struct type tag")
1228                        };
1229                        let type_ = (*struct_tag).into();
1230                        ValueKind::Object {
1231                            type_,
1232                            has_public_transfer: *has_public_transfer,
1233                        }
1234                    } else {
1235                        let abilities = context
1236                            .vm
1237                            .get_runtime()
1238                            .get_type_abilities(inner)
1239                            .map_err(|e| context.convert_vm_error(e))?;
1240                        ValueKind::Raw((**inner).clone(), abilities)
1241                    };
1242                    by_mut_ref.push((idx as LocalIndex, object_info));
1243                    (value, inner)
1244                }
1245                Type::Reference(inner) => (context.borrow_arg(idx, arg, param_ty)?, inner),
1246                t => {
1247                    let value = context.by_value_arg(command_kind, idx, arg)?;
1248                    (value, t)
1249                }
1250            };
1251            if matches!(
1252                function_kind,
1253                FunctionKind::PrivateEntry | FunctionKind::Init
1254            ) && value.was_used_in_non_entry_move_call()
1255            {
1256                return Err(command_argument_error(
1257                    CommandArgumentError::InvalidArgumentToPrivateEntryFunction,
1258                    idx,
1259                ));
1260            }
1261            check_param_type::<Mode>(context, idx, &value, non_ref_param_ty)?;
1262            let bytes = {
1263                let mut v = vec![];
1264                value.write_bcs_bytes(&mut v);
1265                v
1266            };
1267            serialized_args.push(bytes);
1268        }
1269        Ok((tx_ctx_kind, by_mut_ref, serialized_args))
1270    }
1271
1272    /// checks that the value is compatible with the specified type
1273    fn check_param_type<Mode: ExecutionMode>(
1274        context: &mut ExecutionContext<'_, '_, '_>,
1275        idx: usize,
1276        value: &Value,
1277        param_ty: &Type,
1278    ) -> Result<(), ExecutionError> {
1279        match value {
1280            // For dev-spect, allow any BCS bytes. This does mean internal invariants for types can
1281            // be violated (like for string or Option)
1282            Value::Raw(RawValueType::Any, _) if Mode::allow_arbitrary_values() => return Ok(()),
1283            // Any means this was just some bytes passed in as an argument (as opposed to being
1284            // generated from a Move function). Meaning we only allow "primitive" values
1285            // and might need to run validation in addition to the BCS layout
1286            Value::Raw(RawValueType::Any, bytes) => {
1287                let Some(layout) = primitive_serialization_layout(context, param_ty)? else {
1288                    let msg = format!(
1289                        "Non-primitive argument at index {}. If it is an object, it must be \
1290                        populated by an object",
1291                        idx,
1292                    );
1293                    return Err(ExecutionError::new_with_source(
1294                        ExecutionErrorKind::command_argument_error(
1295                            CommandArgumentError::InvalidUsageOfPureArg,
1296                            idx as u16,
1297                        ),
1298                        msg,
1299                    ));
1300                };
1301                bcs_argument_validate(bytes, idx as u16, layout)?;
1302                return Ok(());
1303            }
1304            Value::Raw(RawValueType::Loaded { ty, abilities, .. }, _) => {
1305                assert_invariant!(
1306                    Mode::allow_arbitrary_values() || !abilities.has_key(),
1307                    "Raw value should never be an object"
1308                );
1309                if ty != param_ty {
1310                    return Err(command_argument_error(
1311                        CommandArgumentError::TypeMismatch,
1312                        idx,
1313                    ));
1314                }
1315            }
1316            Value::Object(obj) => {
1317                let ty = &obj.type_;
1318                if ty != param_ty {
1319                    return Err(command_argument_error(
1320                        CommandArgumentError::TypeMismatch,
1321                        idx,
1322                    ));
1323                }
1324            }
1325            Value::Receiving(_, _, assigned_type) => {
1326                // If the type has been fixed, make sure the types match up
1327                if let Some(assigned_type) = assigned_type {
1328                    if assigned_type != param_ty {
1329                        return Err(command_argument_error(
1330                            CommandArgumentError::TypeMismatch,
1331                            idx,
1332                        ));
1333                    }
1334                }
1335
1336                // Now make sure the param type is a struct instantiation of the receiving struct
1337                let Type::DatatypeInstantiation(inst) = param_ty else {
1338                    return Err(command_argument_error(
1339                        CommandArgumentError::TypeMismatch,
1340                        idx,
1341                    ));
1342                };
1343                let (sidx, targs) = &**inst;
1344                let Some(s) = context.vm.get_runtime().get_type(*sidx) else {
1345                    invariant_violation!("sui::transfer::Receiving struct not found in session")
1346                };
1347                let resolved_struct = get_datatype_ident(&s);
1348
1349                if resolved_struct != RESOLVED_RECEIVING_STRUCT || targs.len() != 1 {
1350                    return Err(command_argument_error(
1351                        CommandArgumentError::TypeMismatch,
1352                        idx,
1353                    ));
1354                }
1355            }
1356        }
1357        Ok(())
1358    }
1359
1360    fn get_datatype_ident(s: &CachedDatatype) -> (&AccountAddress, &IdentStr, &IdentStr) {
1361        let module_id = &s.defining_id;
1362        let struct_name = &s.name;
1363        (
1364            module_id.address(),
1365            module_id.name(),
1366            struct_name.as_ident_str(),
1367        )
1368    }
1369
1370    // Returns Some(kind) if the type is a reference to the TxnContext. kind being Mutable with
1371    // a MutableReference, and Immutable otherwise.
1372    // Returns None for all other types
1373    pub fn is_tx_context(
1374        context: &mut ExecutionContext<'_, '_, '_>,
1375        t: &Type,
1376    ) -> Result<TxContextKind, ExecutionError> {
1377        let (is_mut, inner) = match t {
1378            Type::MutableReference(inner) => (true, inner),
1379            Type::Reference(inner) => (false, inner),
1380            _ => return Ok(TxContextKind::None),
1381        };
1382        let Type::Datatype(idx) = &**inner else {
1383            return Ok(TxContextKind::None);
1384        };
1385        let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1386            invariant_violation!("Loaded struct not found")
1387        };
1388        let (module_addr, module_name, struct_name) = get_datatype_ident(&s);
1389        let is_tx_context_type = module_addr == &SUI_FRAMEWORK_ADDRESS
1390            && module_name == TX_CONTEXT_MODULE_NAME
1391            && struct_name == TX_CONTEXT_STRUCT_NAME;
1392        Ok(if is_tx_context_type {
1393            if is_mut {
1394                TxContextKind::Mutable
1395            } else {
1396                TxContextKind::Immutable
1397            }
1398        } else {
1399            TxContextKind::None
1400        })
1401    }
1402
1403    /// Returns Some(layout) iff it is a primitive, an ID, a String, or an option/vector of a valid type
1404    fn primitive_serialization_layout(
1405        context: &mut ExecutionContext<'_, '_, '_>,
1406        param_ty: &Type,
1407    ) -> Result<Option<PrimitiveArgumentLayout>, ExecutionError> {
1408        Ok(match param_ty {
1409            Type::Signer => return Ok(None),
1410            Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => {
1411                invariant_violation!("references and type parameters should be checked elsewhere")
1412            }
1413            Type::Bool => Some(PrimitiveArgumentLayout::Bool),
1414            Type::U8 => Some(PrimitiveArgumentLayout::U8),
1415            Type::U16 => Some(PrimitiveArgumentLayout::U16),
1416            Type::U32 => Some(PrimitiveArgumentLayout::U32),
1417            Type::U64 => Some(PrimitiveArgumentLayout::U64),
1418            Type::U128 => Some(PrimitiveArgumentLayout::U128),
1419            Type::U256 => Some(PrimitiveArgumentLayout::U256),
1420            Type::Address => Some(PrimitiveArgumentLayout::Address),
1421
1422            Type::Vector(inner) => {
1423                let info_opt = primitive_serialization_layout(context, inner)?;
1424                info_opt.map(|layout| PrimitiveArgumentLayout::Vector(Box::new(layout)))
1425            }
1426            Type::DatatypeInstantiation(inst) => {
1427                let (idx, targs) = &**inst;
1428                let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1429                    invariant_violation!("Loaded struct not found")
1430                };
1431                let resolved_struct = get_datatype_ident(&s);
1432                // is option of a string
1433                if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
1434                    let info_opt = primitive_serialization_layout(context, &targs[0])?;
1435                    info_opt.map(|layout| PrimitiveArgumentLayout::Option(Box::new(layout)))
1436                } else {
1437                    None
1438                }
1439            }
1440            Type::Datatype(idx) => {
1441                let Some(s) = context.vm.get_runtime().get_type(*idx) else {
1442                    invariant_violation!("Loaded struct not found")
1443                };
1444                let resolved_struct = get_datatype_ident(&s);
1445                if resolved_struct == RESOLVED_SUI_ID {
1446                    Some(PrimitiveArgumentLayout::Address)
1447                } else if resolved_struct == RESOLVED_ASCII_STR {
1448                    Some(PrimitiveArgumentLayout::Ascii)
1449                } else if resolved_struct == RESOLVED_UTF8_STR {
1450                    Some(PrimitiveArgumentLayout::UTF8)
1451                } else {
1452                    None
1453                }
1454            }
1455        })
1456    }
1457
1458    /***************************************************************************************************
1459     * Special serialization formats
1460     **************************************************************************************************/
1461
1462    /// Special enum for values that need additional validation, in other words
1463    /// There is validation to do on top of the BCS layout. Currently only needed for
1464    /// strings
1465    #[derive(Debug)]
1466    pub enum PrimitiveArgumentLayout {
1467        /// An option
1468        Option(Box<PrimitiveArgumentLayout>),
1469        /// A vector
1470        Vector(Box<PrimitiveArgumentLayout>),
1471        /// An ASCII encoded string
1472        Ascii,
1473        /// A UTF8 encoded string
1474        UTF8,
1475        // needed for Option validation
1476        Bool,
1477        U8,
1478        U16,
1479        U32,
1480        U64,
1481        U128,
1482        U256,
1483        Address,
1484    }
1485
1486    impl PrimitiveArgumentLayout {
1487        /// returns true iff all BCS compatible bytes are actually values for this type.
1488        /// For example, this function returns false for Option and Strings since they need additional
1489        /// validation.
1490        pub fn bcs_only(&self) -> bool {
1491            match self {
1492                // have additional restrictions past BCS
1493                PrimitiveArgumentLayout::Option(_)
1494                | PrimitiveArgumentLayout::Ascii
1495                | PrimitiveArgumentLayout::UTF8 => false,
1496                // Move primitives are BCS compatible and do not need additional validation
1497                PrimitiveArgumentLayout::Bool
1498                | PrimitiveArgumentLayout::U8
1499                | PrimitiveArgumentLayout::U16
1500                | PrimitiveArgumentLayout::U32
1501                | PrimitiveArgumentLayout::U64
1502                | PrimitiveArgumentLayout::U128
1503                | PrimitiveArgumentLayout::U256
1504                | PrimitiveArgumentLayout::Address => true,
1505                // vector only needs validation if it's inner type does
1506                PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(),
1507            }
1508        }
1509    }
1510
1511    /// Checks the bytes against the `SpecialArgumentLayout` using `bcs`. It does not actually generate
1512    /// the deserialized value, only walks the bytes. While not necessary if the layout does not contain
1513    /// special arguments (e.g. Option or String) we check the BCS bytes for predictability
1514    pub fn bcs_argument_validate(
1515        bytes: &[u8],
1516        idx: u16,
1517        layout: PrimitiveArgumentLayout,
1518    ) -> Result<(), ExecutionError> {
1519        bcs::from_bytes_seed(&layout, bytes).map_err(|_| {
1520            ExecutionError::new_with_source(
1521                ExecutionErrorKind::command_argument_error(
1522                    CommandArgumentError::InvalidBCSBytes,
1523                    idx,
1524                ),
1525                format!("Function expects {layout} but provided argument's value does not match",),
1526            )
1527        })
1528    }
1529
1530    impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout {
1531        type Value = ();
1532        fn deserialize<D: serde::de::Deserializer<'d>>(
1533            self,
1534            deserializer: D,
1535        ) -> Result<Self::Value, D::Error> {
1536            use serde::de::Error;
1537            match self {
1538                PrimitiveArgumentLayout::Ascii => {
1539                    let s: &str = serde::Deserialize::deserialize(deserializer)?;
1540                    if !s.is_ascii() {
1541                        Err(D::Error::custom("not an ascii string"))
1542                    } else {
1543                        Ok(())
1544                    }
1545                }
1546                PrimitiveArgumentLayout::UTF8 => {
1547                    deserializer.deserialize_string(serde::de::IgnoredAny)?;
1548                    Ok(())
1549                }
1550                PrimitiveArgumentLayout::Option(layout) => {
1551                    deserializer.deserialize_option(OptionElementVisitor(layout))
1552                }
1553                PrimitiveArgumentLayout::Vector(layout) => {
1554                    deserializer.deserialize_seq(VectorElementVisitor(layout))
1555                }
1556                // primitive move value cases, which are hit to make sure the correct number of bytes
1557                // are removed for elements of an option/vector
1558                PrimitiveArgumentLayout::Bool => {
1559                    deserializer.deserialize_bool(serde::de::IgnoredAny)?;
1560                    Ok(())
1561                }
1562                PrimitiveArgumentLayout::U8 => {
1563                    deserializer.deserialize_u8(serde::de::IgnoredAny)?;
1564                    Ok(())
1565                }
1566                PrimitiveArgumentLayout::U16 => {
1567                    deserializer.deserialize_u16(serde::de::IgnoredAny)?;
1568                    Ok(())
1569                }
1570                PrimitiveArgumentLayout::U32 => {
1571                    deserializer.deserialize_u32(serde::de::IgnoredAny)?;
1572                    Ok(())
1573                }
1574                PrimitiveArgumentLayout::U64 => {
1575                    deserializer.deserialize_u64(serde::de::IgnoredAny)?;
1576                    Ok(())
1577                }
1578                PrimitiveArgumentLayout::U128 => {
1579                    deserializer.deserialize_u128(serde::de::IgnoredAny)?;
1580                    Ok(())
1581                }
1582                PrimitiveArgumentLayout::U256 => {
1583                    U256::deserialize(deserializer)?;
1584                    Ok(())
1585                }
1586                PrimitiveArgumentLayout::Address => {
1587                    SuiAddress::deserialize(deserializer)?;
1588                    Ok(())
1589                }
1590            }
1591        }
1592    }
1593
1594    struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout);
1595
1596    impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> {
1597        type Value = ();
1598
1599        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1600            formatter.write_str("Vector")
1601        }
1602
1603        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1604        where
1605            A: serde::de::SeqAccess<'d>,
1606        {
1607            while seq.next_element_seed(self.0)?.is_some() {}
1608            Ok(())
1609        }
1610    }
1611
1612    struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout);
1613
1614    impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> {
1615        type Value = ();
1616
1617        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1618            formatter.write_str("Option")
1619        }
1620
1621        fn visit_none<E>(self) -> Result<Self::Value, E>
1622        where
1623            E: serde::de::Error,
1624        {
1625            Ok(())
1626        }
1627
1628        fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1629        where
1630            D: serde::Deserializer<'d>,
1631        {
1632            self.0.deserialize(deserializer)
1633        }
1634    }
1635
1636    impl fmt::Display for PrimitiveArgumentLayout {
1637        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1638            match self {
1639                PrimitiveArgumentLayout::Vector(inner) => {
1640                    write!(f, "vector<{inner}>")
1641                }
1642                PrimitiveArgumentLayout::Option(inner) => {
1643                    write!(f, "std::option::Option<{inner}>")
1644                }
1645                PrimitiveArgumentLayout::Ascii => {
1646                    write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2)
1647                }
1648                PrimitiveArgumentLayout::UTF8 => {
1649                    write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2)
1650                }
1651                PrimitiveArgumentLayout::Bool => write!(f, "bool"),
1652                PrimitiveArgumentLayout::U8 => write!(f, "u8"),
1653                PrimitiveArgumentLayout::U16 => write!(f, "u16"),
1654                PrimitiveArgumentLayout::U32 => write!(f, "u32"),
1655                PrimitiveArgumentLayout::U64 => write!(f, "u64"),
1656                PrimitiveArgumentLayout::U128 => write!(f, "u128"),
1657                PrimitiveArgumentLayout::U256 => write!(f, "u256"),
1658                PrimitiveArgumentLayout::Address => write!(f, "address"),
1659            }
1660        }
1661    }
1662}