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