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