sui_move_natives_latest/
config.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    NativesCostTable, abstract_size, get_extension, get_extension_mut,
6    object_runtime::ObjectRuntime,
7};
8use move_binary_format::errors::{PartialVMError, PartialVMResult};
9use move_core_types::{
10    account_address::AccountAddress, gas_algebra::InternalGas, language_storage::StructTag,
11    runtime_value as R, vm_status::StatusCode,
12};
13use move_vm_runtime::native_charge_gas_early_exit;
14use move_vm_runtime::native_functions::NativeContext;
15use move_vm_types::{
16    loaded_data::runtime_types::Type,
17    natives::function::NativeResult,
18    pop_arg,
19    values::{Struct, Value, Vector},
20};
21use smallvec::smallvec;
22use std::collections::VecDeque;
23use sui_types::{TypeTag, base_types::MoveObjectType};
24use tracing::{error, instrument};
25
26const E_BCS_SERIALIZATION_FAILURE: u64 = 2;
27
28#[derive(Clone)]
29pub struct ConfigReadSettingImplCostParams {
30    pub config_read_setting_impl_cost_base: Option<InternalGas>,
31    pub config_read_setting_impl_cost_per_byte: Option<InternalGas>,
32}
33
34#[instrument(level = "trace", skip_all)]
35pub fn read_setting_impl(
36    context: &mut NativeContext,
37    mut ty_args: Vec<Type>,
38    mut args: VecDeque<Value>,
39) -> PartialVMResult<NativeResult> {
40    assert_eq!(ty_args.len(), 4);
41    assert_eq!(args.len(), 3);
42
43    let ConfigReadSettingImplCostParams {
44        config_read_setting_impl_cost_base,
45        config_read_setting_impl_cost_per_byte,
46    } = get_extension!(context, NativesCostTable)?
47        .config_read_setting_impl_cost_params
48        .clone();
49
50    let config_read_setting_impl_cost_base =
51        config_read_setting_impl_cost_base.ok_or_else(|| {
52            PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
53                .with_message("gas cost is not set".to_string())
54        })?;
55    let config_read_setting_impl_cost_per_byte = config_read_setting_impl_cost_per_byte
56        .ok_or_else(|| {
57            PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
58                .with_message("gas cost is not set".to_string())
59        })?;
60    // Charge base fee
61    native_charge_gas_early_exit!(context, config_read_setting_impl_cost_base);
62
63    let value_ty = ty_args.pop().unwrap();
64    let setting_data_value_ty = ty_args.pop().unwrap();
65    let setting_value_ty = ty_args.pop().unwrap();
66    let field_setting_ty = ty_args.pop().unwrap();
67
68    let current_epoch = pop_arg!(args, u64);
69    let name_df_addr = pop_arg!(args, AccountAddress);
70    let config_addr = pop_arg!(args, AccountAddress);
71
72    let field_setting_tag: StructTag = match context.type_to_type_tag(&field_setting_ty)? {
73        TypeTag::Struct(s) => *s,
74        _ => {
75            return Err(
76                PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
77                    .with_message("Sui verifier guarantees this is a struct".to_string()),
78            );
79        }
80    };
81    let Some(field_setting_layout) = context.type_to_type_layout(&field_setting_ty)? else {
82        return Ok(NativeResult::err(
83            context.gas_used(),
84            E_BCS_SERIALIZATION_FAILURE,
85        ));
86    };
87    let object_runtime: &mut ObjectRuntime = get_extension_mut!(context)?;
88
89    let read_value_opt = consistent_value_before_current_epoch(
90        object_runtime,
91        field_setting_tag,
92        &field_setting_layout,
93        &setting_value_ty,
94        &setting_data_value_ty,
95        &value_ty,
96        config_addr,
97        name_df_addr,
98        current_epoch,
99    )?;
100
101    let size = abstract_size(object_runtime.protocol_config, &read_value_opt);
102
103    native_charge_gas_early_exit!(
104        context,
105        config_read_setting_impl_cost_per_byte * u64::from(size).into()
106    );
107
108    Ok(NativeResult::ok(
109        context.gas_used(),
110        smallvec![read_value_opt],
111    ))
112}
113
114fn consistent_value_before_current_epoch(
115    object_runtime: &mut ObjectRuntime,
116    field_setting_tag: StructTag,
117    field_setting_layout: &R::MoveTypeLayout,
118    _setting_value_ty: &Type,
119    setting_data_value_ty: &Type,
120    value_ty: &Type,
121    config_addr: AccountAddress,
122    name_df_addr: AccountAddress,
123    current_epoch: u64,
124) -> PartialVMResult<Value> {
125    let field_setting_obj_ty = MoveObjectType::from(field_setting_tag);
126    let Some(field) = object_runtime.config_setting_unsequenced_read(
127        config_addr.into(),
128        name_df_addr.into(),
129        field_setting_layout,
130        &field_setting_obj_ty,
131    ) else {
132        return option_none(value_ty);
133    };
134
135    let [_id, _name, setting]: [Value; 3] = unpack_struct(field)?;
136    let [data_opt]: [Value; 1] = unpack_struct(setting)?;
137    let data = match unpack_option(data_opt, setting_data_value_ty)? {
138        None => {
139            error!(
140                "
141                SettingData is none.
142                config_addr: {config_addr},
143                name_df_addr: {name_df_addr},
144                field_setting_obj_ty: {field_setting_obj_ty:?}",
145            );
146            return option_none(value_ty);
147        }
148        Some(data) => data,
149    };
150    let [newer_value_epoch, newer_value, older_value_opt]: [Value; 3] = unpack_struct(data)?;
151    let newer_value_epoch: u64 = newer_value_epoch.value_as()?;
152    debug_assert!(
153        unpack_option(newer_value.copy_value()?, value_ty)?.is_some()
154            || unpack_option(older_value_opt.copy_value()?, value_ty)?.is_some()
155    );
156    Ok(if current_epoch > newer_value_epoch {
157        newer_value
158    } else {
159        older_value_opt
160    })
161}
162
163fn unpack_struct<const N: usize>(s: Value) -> PartialVMResult<[Value; N]> {
164    let s: Struct = s.value_as()?;
165    s.unpack()?.collect::<Vec<_>>().try_into().map_err(|e| {
166        PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
167            .with_message(format!("struct expected to have {N} fields: {e:?}"))
168    })
169}
170
171fn unpack_option(option: Value, type_param: &Type) -> PartialVMResult<Option<Value>> {
172    let [vec_value]: [Value; 1] = unpack_struct(option)?;
173    let vec: Vector = vec_value.value_as()?;
174    Ok(if vec.elem_len() == 0 {
175        None
176    } else {
177        let [elem]: [Value; 1] = vec.unpack(type_param, 1)?.try_into().map_err(|e| {
178            PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
179                .with_message(format!("vector expected to have one element: {e:?}"))
180        })?;
181        Some(elem)
182    })
183}
184
185fn option_none(type_param: &Type) -> PartialVMResult<Value> {
186    Ok(Value::struct_(Struct::pack(vec![Vector::empty(
187        type_param.try_into()?,
188    )?])))
189}