sui_types/sui_system_state/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use self::sui_system_state_inner_v1::{SuiSystemStateInnerV1, ValidatorV1};
5use self::sui_system_state_summary::{SuiSystemStateSummary, SuiValidatorSummary};
6use crate::base_types::ObjectID;
7use crate::collection_types::Bag;
8use crate::committee::CommitteeWithNetworkMetadata;
9use crate::dynamic_field::{
10    Field, get_dynamic_field_from_store, get_dynamic_field_object_from_store,
11};
12use crate::error::{SuiError, SuiErrorKind};
13use crate::gas::GasCostSummary;
14use crate::object::{MoveObject, Object};
15use crate::storage::ObjectStore;
16use crate::sui_system_state::epoch_start_sui_system_state::EpochStartSystemState;
17use crate::sui_system_state::sui_system_state_inner_v2::SuiSystemStateInnerV2;
18use crate::versioned::Versioned;
19use crate::{MoveTypeTagTrait, SUI_SYSTEM_ADDRESS, SUI_SYSTEM_STATE_OBJECT_ID, id::UID};
20use anyhow::Result;
21use enum_dispatch::enum_dispatch;
22use move_core_types::{ident_str, identifier::IdentStr, language_storage::StructTag};
23use serde::de::DeserializeOwned;
24use serde::{Deserialize, Serialize};
25use std::fmt;
26use sui_protocol_config::{ProtocolConfig, ProtocolVersion};
27
28pub mod epoch_start_sui_system_state;
29pub mod mock;
30pub mod sui_system_state_inner_v1;
31pub mod sui_system_state_inner_v2;
32pub mod sui_system_state_summary;
33
34#[cfg(msim)]
35mod simtest_sui_system_state_inner;
36#[cfg(msim)]
37use self::simtest_sui_system_state_inner::{
38    SimTestSuiSystemStateInnerDeepV2, SimTestSuiSystemStateInnerShallowV2,
39    SimTestSuiSystemStateInnerV1, SimTestValidatorDeepV2, SimTestValidatorV1,
40};
41
42const SUI_SYSTEM_STATE_WRAPPER_STRUCT_NAME: &IdentStr = ident_str!("SuiSystemState");
43
44pub const SUI_SYSTEM_MODULE_NAME: &IdentStr = ident_str!("sui_system");
45pub const SUI_SYSTEM_STATE_INNER_MODULE_NAME: &IdentStr = ident_str!("sui_system_state_inner");
46pub const SUI_SYSTEM_STATE_INNER_V1_STRUCT_NAME: &IdentStr = ident_str!("SuiSystemStateInner");
47pub const SUI_SYSTEM_STATE_INNER_V2_STRUCT_NAME: &IdentStr = ident_str!("SuiSystemStateInnerV2");
48pub const VALIDATOR_MODULE_NAME: &IdentStr = ident_str!("validator");
49pub const VALIDATOR_STRUCT_NAME: &IdentStr = ident_str!("Validator");
50pub const ADVANCE_EPOCH_FUNCTION_NAME: &IdentStr = ident_str!("advance_epoch");
51pub const ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME: &IdentStr = ident_str!("advance_epoch_safe_mode");
52
53#[cfg(msim)]
54pub const SUI_SYSTEM_STATE_SIM_TEST_V1: u64 = 18446744073709551605; // u64::MAX - 10
55#[cfg(msim)]
56pub const SUI_SYSTEM_STATE_SIM_TEST_SHALLOW_V2: u64 = 18446744073709551606; // u64::MAX - 9
57#[cfg(msim)]
58pub const SUI_SYSTEM_STATE_SIM_TEST_DEEP_V2: u64 = 18446744073709551607; // u64::MAX - 8
59
60/// Rust version of the Move sui::sui_system::SuiSystemState type
61/// This represents the object with 0x5 ID.
62/// In Rust, this type should be rarely used since it's just a thin
63/// wrapper used to access the inner object.
64/// Within this module, we use it to determine the current version of the system state inner object type,
65/// so that we could deserialize the inner object correctly.
66/// Outside of this module, we only use it in genesis snapshot and testing.
67#[derive(Debug, Serialize, Deserialize, Clone)]
68pub struct SuiSystemStateWrapper {
69    pub id: UID,
70    pub version: u64,
71}
72
73impl SuiSystemStateWrapper {
74    pub fn type_() -> StructTag {
75        StructTag {
76            address: SUI_SYSTEM_ADDRESS,
77            name: SUI_SYSTEM_STATE_WRAPPER_STRUCT_NAME.to_owned(),
78            module: SUI_SYSTEM_MODULE_NAME.to_owned(),
79            type_params: vec![],
80        }
81    }
82
83    /// Advances epoch in safe mode natively in Rust, without involking Move.
84    /// This ensures that there cannot be any failure from Move and is guaranteed to succeed.
85    /// Returns the old and new inner system state object.
86    pub fn advance_epoch_safe_mode(
87        &self,
88        params: &AdvanceEpochParams,
89        object_store: &dyn ObjectStore,
90        protocol_config: &ProtocolConfig,
91    ) -> (Object, Object) {
92        let id = self.id.id.bytes;
93        let old_field_object = get_dynamic_field_object_from_store(object_store, id, &self.version)
94            .expect("Dynamic field object of wrapper should always be present in the object store");
95        let mut new_field_object = old_field_object.clone();
96        let move_object = new_field_object
97            .data
98            .try_as_move_mut()
99            .expect("Dynamic field object must be a Move object");
100        match self.version {
101            1 => {
102                Self::advance_epoch_safe_mode_impl::<SuiSystemStateInnerV1>(
103                    move_object,
104                    params,
105                    protocol_config,
106                );
107            }
108            2 => {
109                Self::advance_epoch_safe_mode_impl::<SuiSystemStateInnerV2>(
110                    move_object,
111                    params,
112                    protocol_config,
113                );
114            }
115            #[cfg(msim)]
116            SUI_SYSTEM_STATE_SIM_TEST_V1 => {
117                Self::advance_epoch_safe_mode_impl::<SimTestSuiSystemStateInnerV1>(
118                    move_object,
119                    params,
120                    protocol_config,
121                );
122            }
123            #[cfg(msim)]
124            SUI_SYSTEM_STATE_SIM_TEST_SHALLOW_V2 => {
125                Self::advance_epoch_safe_mode_impl::<SimTestSuiSystemStateInnerShallowV2>(
126                    move_object,
127                    params,
128                    protocol_config,
129                );
130            }
131            #[cfg(msim)]
132            SUI_SYSTEM_STATE_SIM_TEST_DEEP_V2 => {
133                Self::advance_epoch_safe_mode_impl::<SimTestSuiSystemStateInnerDeepV2>(
134                    move_object,
135                    params,
136                    protocol_config,
137                );
138            }
139            _ => unreachable!(),
140        }
141        (old_field_object, new_field_object)
142    }
143
144    fn advance_epoch_safe_mode_impl<T>(
145        move_object: &mut MoveObject,
146        params: &AdvanceEpochParams,
147        protocol_config: &ProtocolConfig,
148    ) where
149        T: Serialize + DeserializeOwned + SuiSystemStateTrait,
150    {
151        let mut field: Field<u64, T> =
152            bcs::from_bytes(move_object.contents()).expect("bcs deserialization should never fail");
153        tracing::info!(
154            "Advance epoch safe mode: current epoch: {}, protocol_version: {}, system_state_version: {}",
155            field.value.epoch(),
156            field.value.protocol_version(),
157            field.value.system_state_version()
158        );
159        field.value.advance_epoch_safe_mode(params);
160        tracing::info!(
161            "Safe mode activated. New epoch: {}, protocol_version: {}, system_state_version: {}",
162            field.value.epoch(),
163            field.value.protocol_version(),
164            field.value.system_state_version()
165        );
166        let new_contents = bcs::to_bytes(&field).expect("bcs serialization should never fail");
167        move_object
168            .update_contents_advance_epoch_safe_mode(new_contents, protocol_config)
169            .expect("Update sui system object content cannot fail since it should be small or unbounded");
170    }
171}
172
173/// This is the standard API that all inner system state object type should implement.
174#[enum_dispatch]
175pub trait SuiSystemStateTrait {
176    fn epoch(&self) -> u64;
177    fn reference_gas_price(&self) -> u64;
178    fn protocol_version(&self) -> u64;
179    fn system_state_version(&self) -> u64;
180    fn epoch_start_timestamp_ms(&self) -> u64;
181    fn epoch_duration_ms(&self) -> u64;
182    fn extra_fields(&self) -> &Bag;
183    fn safe_mode(&self) -> bool;
184    fn safe_mode_gas_cost_summary(&self) -> GasCostSummary;
185    fn advance_epoch_safe_mode(&mut self, params: &AdvanceEpochParams);
186    fn get_current_epoch_committee(&self) -> CommitteeWithNetworkMetadata;
187    fn get_pending_active_validators<S: ObjectStore + ?Sized>(
188        &self,
189        object_store: &S,
190    ) -> Result<Vec<SuiValidatorSummary>, SuiError>;
191    fn into_epoch_start_state(self) -> EpochStartSystemState;
192    fn into_sui_system_state_summary(self) -> SuiSystemStateSummary;
193}
194
195/// SuiSystemState provides an abstraction over multiple versions of the inner SuiSystemStateInner object.
196/// This should be the primary interface to the system state object in Rust.
197/// We use enum dispatch to dispatch all methods defined in SuiSystemStateTrait to the actual
198/// implementation in the inner types.
199#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
200#[enum_dispatch(SuiSystemStateTrait)]
201pub enum SuiSystemState {
202    V1(SuiSystemStateInnerV1),
203    V2(SuiSystemStateInnerV2),
204    #[cfg(msim)]
205    SimTestV1(SimTestSuiSystemStateInnerV1),
206    #[cfg(msim)]
207    SimTestShallowV2(SimTestSuiSystemStateInnerShallowV2),
208    #[cfg(msim)]
209    SimTestDeepV2(SimTestSuiSystemStateInnerDeepV2),
210}
211
212/// This is the fixed type used by genesis.
213pub type SuiSystemStateInnerGenesis = SuiSystemStateInnerV1;
214pub type SuiValidatorGenesis = ValidatorV1;
215
216impl SuiSystemState {
217    /// Always return the version that we will be using for genesis.
218    /// Genesis always uses this version regardless of the current version.
219    /// Note that since it's possible for the actual genesis of the network to diverge from the
220    /// genesis of the latest Rust code, it's important that we only use this for tooling purposes.
221    pub fn into_genesis_version_for_tooling(self) -> SuiSystemStateInnerGenesis {
222        match self {
223            SuiSystemState::V1(inner) => inner,
224            _ => unreachable!(),
225        }
226    }
227
228    pub fn version(&self) -> u64 {
229        self.system_state_version()
230    }
231}
232
233pub fn get_sui_system_state_wrapper(
234    object_store: &dyn ObjectStore,
235) -> Result<SuiSystemStateWrapper, SuiError> {
236    let wrapper = object_store
237        .get_object(&SUI_SYSTEM_STATE_OBJECT_ID)
238        // Don't panic here on None because object_store is a generic store.
239        .ok_or_else(|| {
240            SuiErrorKind::SuiSystemStateReadError(
241                "SuiSystemStateWrapper object not found".to_owned(),
242            )
243        })?;
244    let move_object = wrapper.data.try_as_move().ok_or_else(|| {
245        SuiErrorKind::SuiSystemStateReadError(
246            "SuiSystemStateWrapper object must be a Move object".to_owned(),
247        )
248    })?;
249    let result = bcs::from_bytes::<SuiSystemStateWrapper>(move_object.contents())
250        .map_err(|err| SuiErrorKind::SuiSystemStateReadError(err.to_string()))?;
251    Ok(result)
252}
253
254pub fn get_sui_system_state(object_store: &dyn ObjectStore) -> Result<SuiSystemState, SuiError> {
255    let wrapper = get_sui_system_state_wrapper(object_store)?;
256    let id = wrapper.id.id.bytes;
257    match wrapper.version {
258        1 => {
259            let result: SuiSystemStateInnerV1 =
260                get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
261                    |err| {
262                        SuiErrorKind::DynamicFieldReadError(format!(
263                            "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
264                            id, wrapper.version, err
265                        ))
266                    },
267                )?;
268            Ok(SuiSystemState::V1(result))
269        }
270        2 => {
271            let result: SuiSystemStateInnerV2 =
272                get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
273                    |err| {
274                        SuiErrorKind::DynamicFieldReadError(format!(
275                            "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
276                            id, wrapper.version, err
277                        ))
278                    },
279                )?;
280            Ok(SuiSystemState::V2(result))
281        }
282        #[cfg(msim)]
283        SUI_SYSTEM_STATE_SIM_TEST_V1 => {
284            let result: SimTestSuiSystemStateInnerV1 =
285                get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
286                    |err| {
287                        SuiErrorKind::DynamicFieldReadError(format!(
288                            "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
289                            id, wrapper.version, err
290                        ))
291                    },
292                )?;
293            Ok(SuiSystemState::SimTestV1(result))
294        }
295        #[cfg(msim)]
296        SUI_SYSTEM_STATE_SIM_TEST_SHALLOW_V2 => {
297            let result: SimTestSuiSystemStateInnerShallowV2 =
298                get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
299                    |err| {
300                        SuiErrorKind::DynamicFieldReadError(format!(
301                            "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
302                            id, wrapper.version, err
303                        ))
304                    },
305                )?;
306            Ok(SuiSystemState::SimTestShallowV2(result))
307        }
308        #[cfg(msim)]
309        SUI_SYSTEM_STATE_SIM_TEST_DEEP_V2 => {
310            let result: SimTestSuiSystemStateInnerDeepV2 =
311                get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
312                    |err| {
313                        SuiErrorKind::DynamicFieldReadError(format!(
314                            "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
315                            id, wrapper.version, err
316                        ))
317                    },
318                )?;
319            Ok(SuiSystemState::SimTestDeepV2(result))
320        }
321        _ => Err(SuiErrorKind::SuiSystemStateReadError(format!(
322            "Unsupported SuiSystemState version: {}",
323            wrapper.version
324        ))
325        .into()),
326    }
327}
328
329/// Given a system state type version, and the ID of the table, along with a key, retrieve the
330/// dynamic field as a Validator type. We need the version to determine which inner type to use for
331/// the Validator type. This is assuming that the validator is stored in the table as
332/// ValidatorWrapper type.
333pub fn get_validator_from_table<K>(
334    object_store: &dyn ObjectStore,
335    table_id: ObjectID,
336    key: &K,
337) -> Result<SuiValidatorSummary, SuiError>
338where
339    K: Clone + MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug,
340{
341    let field: ValidatorWrapper = get_dynamic_field_from_store(object_store, table_id, key)
342        .map_err(|err| {
343            SuiErrorKind::SuiSystemStateReadError(format!(
344                "Failed to load validator wrapper from table: {:?}",
345                err
346            ))
347        })?;
348    let versioned = field.inner;
349    let version = versioned.version;
350    match version {
351        1 => {
352            let validator: ValidatorV1 =
353                get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
354                    .map_err(|err| {
355                        SuiErrorKind::SuiSystemStateReadError(format!(
356                            "Failed to load inner validator from the wrapper: {:?}",
357                            err
358                        ))
359                    })?;
360            Ok(validator.into_sui_validator_summary())
361        }
362        #[cfg(msim)]
363        SUI_SYSTEM_STATE_SIM_TEST_V1 => {
364            let validator: SimTestValidatorV1 =
365                get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
366                    .map_err(|err| {
367                        SuiErrorKind::SuiSystemStateReadError(format!(
368                            "Failed to load inner validator from the wrapper: {:?}",
369                            err
370                        ))
371                    })?;
372            Ok(validator.into_sui_validator_summary())
373        }
374        #[cfg(msim)]
375        SUI_SYSTEM_STATE_SIM_TEST_DEEP_V2 => {
376            let validator: SimTestValidatorDeepV2 =
377                get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
378                    .map_err(|err| {
379                        SuiErrorKind::SuiSystemStateReadError(format!(
380                            "Failed to load inner validator from the wrapper: {:?}",
381                            err
382                        ))
383                    })?;
384            Ok(validator.into_sui_validator_summary())
385        }
386        _ => Err(SuiErrorKind::SuiSystemStateReadError(format!(
387            "Unsupported Validator version: {}",
388            version
389        ))
390        .into()),
391    }
392}
393
394pub fn get_validators_from_table_vec<S, ValidatorType>(
395    object_store: &S,
396    table_id: ObjectID,
397    table_size: u64,
398) -> Result<Vec<ValidatorType>, SuiError>
399where
400    S: ObjectStore + ?Sized,
401    ValidatorType: Serialize + DeserializeOwned,
402{
403    let mut validators = vec![];
404    for i in 0..table_size {
405        let validator: ValidatorType = get_dynamic_field_from_store(&object_store, table_id, &i)
406            .map_err(|err| {
407                SuiErrorKind::SuiSystemStateReadError(format!(
408                    "Failed to load validator from table: {:?}",
409                    err
410                ))
411            })?;
412        validators.push(validator);
413    }
414    Ok(validators)
415}
416
417#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)]
418pub struct PoolTokenExchangeRate {
419    sui_amount: u64,
420    pool_token_amount: u64,
421}
422
423impl PoolTokenExchangeRate {
424    /// Rate of the staking pool, pool token amount : Sui amount
425    pub fn rate(&self) -> f64 {
426        if self.sui_amount == 0 {
427            1_f64
428        } else {
429            self.pool_token_amount as f64 / self.sui_amount as f64
430        }
431    }
432
433    pub fn new(sui_amount: u64, pool_token_amount: u64) -> Self {
434        Self {
435            sui_amount,
436            pool_token_amount,
437        }
438    }
439}
440
441#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
442pub struct ValidatorWrapper {
443    pub inner: Versioned,
444}
445
446#[derive(Debug)]
447pub struct AdvanceEpochParams {
448    pub epoch: u64,
449    pub next_protocol_version: ProtocolVersion,
450    pub storage_charge: u64,
451    pub computation_charge: u64,
452    pub storage_rebate: u64,
453    pub non_refundable_storage_fee: u64,
454    pub storage_fund_reinvest_rate: u64,
455    pub reward_slashing_rate: u64,
456    pub epoch_start_timestamp_ms: u64,
457}
458
459#[cfg(msim)]
460pub mod advance_epoch_result_injection {
461    use crate::{
462        committee::EpochId,
463        error::{ExecutionError, ExecutionErrorKind},
464        execution::ResultWithTimings,
465    };
466    use std::cell::RefCell;
467
468    thread_local! {
469        /// Override the result of advance_epoch in the range [start, end).
470        static OVERRIDE: RefCell<Option<(EpochId, EpochId)>>  = RefCell::new(None);
471    }
472
473    /// Override the result of advance_epoch transaction if new epoch is in the provided range [start, end).
474    pub fn set_override(value: Option<(EpochId, EpochId)>) {
475        OVERRIDE.with(|o| *o.borrow_mut() = value);
476    }
477
478    /// This function is used to modify the result of advance_epoch transaction for testing.
479    /// If the override is set, the result will be an execution error, otherwise the original result will be returned.
480    pub fn maybe_modify_result(
481        result: ResultWithTimings<(), ExecutionError>,
482        current_epoch: EpochId,
483    ) -> ResultWithTimings<(), ExecutionError> {
484        if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
485            if current_epoch >= start && current_epoch < end {
486                return Err((
487                    ExecutionError::new(ExecutionErrorKind::FunctionNotFound, None),
488                    vec![],
489                ));
490            }
491        }
492        result
493    }
494
495    // For old execution versions that don't report timings
496    pub fn maybe_modify_result_legacy(
497        result: Result<(), ExecutionError>,
498        current_epoch: EpochId,
499    ) -> Result<(), ExecutionError> {
500        if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
501            if current_epoch >= start && current_epoch < end {
502                return Err(ExecutionError::new(
503                    ExecutionErrorKind::FunctionNotFound,
504                    None,
505                ));
506            }
507        }
508        result
509    }
510}