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