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