iota_types/iota_system_state/
mod.rs

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