sui_move_natives_v1/
types.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use move_binary_format::errors::PartialVMResult;
5use move_core_types::{
6    gas_algebra::InternalGas,
7    language_storage::TypeTag,
8    runtime_value::{MoveStructLayout, MoveTypeLayout},
9};
10use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
11use move_vm_types::{
12    loaded_data::runtime_types::Type, natives::function::NativeResult, values::Value,
13};
14use smallvec::smallvec;
15use std::collections::VecDeque;
16
17use crate::NativesCostTable;
18
19pub(crate) fn is_otw_struct(struct_layout: &MoveStructLayout, type_tag: &TypeTag) -> bool {
20    let has_one_bool_field = matches!(struct_layout.0.as_slice(), [MoveTypeLayout::Bool]);
21
22    // If a struct type has the same name as the module that defines it but capitalized, and it has
23    // a single field of type bool, it means that it's a one-time witness type. The remaining
24    // properties of a one-time witness type are checked in the one_time_witness_verifier pass in
25    // the Sui bytecode verifier (a type with this name and with a single bool field that does not
26    // have all the remaining properties of a one-time witness type will cause a verifier error).
27    matches!(
28        type_tag,
29        TypeTag::Struct(struct_tag) if has_one_bool_field && struct_tag.name.to_string() == struct_tag.module.to_string().to_ascii_uppercase())
30}
31
32#[derive(Clone)]
33pub struct TypesIsOneTimeWitnessCostParams {
34    pub types_is_one_time_witness_cost_base: InternalGas,
35    pub types_is_one_time_witness_type_tag_cost_per_byte: InternalGas,
36    pub types_is_one_time_witness_type_cost_per_byte: InternalGas,
37}
38/***************************************************************************************************
39 * native fun is_one_time_witness
40 * Implementation of the Move native function `is_one_time_witness<T: drop>(_: &T): bool`
41 *   gas cost: types_is_one_time_witness_cost_base                        | base cost as this can be expensive oper
42 *              + types_is_one_time_witness_type_tag_cost_per_byte * type_tag.size()        | cost per byte of converting type to type tag
43 *              + types_is_one_time_witness_type_cost_per_byte * ty.size()                  | cost per byte of converting type to type layout
44 **************************************************************************************************/
45pub fn is_one_time_witness(
46    context: &mut NativeContext,
47    mut ty_args: Vec<Type>,
48    args: VecDeque<Value>,
49) -> PartialVMResult<NativeResult> {
50    debug_assert!(ty_args.len() == 1);
51    debug_assert!(args.len() == 1);
52
53    let type_is_one_time_witness_cost_params = context
54        .extensions_mut()
55        .get::<NativesCostTable>()
56        .type_is_one_time_witness_cost_params
57        .clone();
58
59    native_charge_gas_early_exit!(
60        context,
61        type_is_one_time_witness_cost_params.types_is_one_time_witness_cost_base
62    );
63
64    // unwrap safe because the interface of native function guarantees it.
65    let ty = ty_args.pop().unwrap();
66
67    native_charge_gas_early_exit!(
68        context,
69        type_is_one_time_witness_cost_params.types_is_one_time_witness_type_cost_per_byte
70            * u64::from(ty.size()).into()
71    );
72
73    let type_tag = context.type_to_type_tag(&ty)?;
74    native_charge_gas_early_exit!(
75        context,
76        type_is_one_time_witness_cost_params.types_is_one_time_witness_type_tag_cost_per_byte
77            * u64::from(type_tag.abstract_size_for_gas_metering()).into()
78    );
79
80    let type_layout = context.type_to_type_layout(&ty)?;
81
82    let cost = context.gas_used();
83    let Some(MoveTypeLayout::Struct(struct_layout)) = type_layout else {
84        return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)]));
85    };
86
87    let is_otw = is_otw_struct(&struct_layout, &type_tag);
88
89    Ok(NativeResult::ok(cost, smallvec![Value::bool(is_otw)]))
90}