iota_core/authority/
epoch_start_configuration.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 enum_dispatch::enum_dispatch;
8use iota_config::{ExecutionCacheConfig, NodeConfig};
9use iota_types::{
10    authenticator_state::get_authenticator_state_obj_initial_shared_version,
11    base_types::SequenceNumber,
12    bridge::{get_bridge_obj_initial_shared_version, is_bridge_committee_initiated},
13    deny_list_v1::get_deny_list_obj_initial_shared_version,
14    epoch_data::EpochData,
15    error::IotaResult,
16    iota_system_state::epoch_start_iota_system_state::{
17        EpochStartSystemState, EpochStartSystemStateTrait,
18    },
19    messages_checkpoint::{CheckpointDigest, CheckpointTimestamp},
20    randomness_state::get_randomness_state_obj_initial_shared_version,
21    storage::ObjectStore,
22};
23use serde::{Deserialize, Serialize};
24
25use crate::execution_cache::{ExecutionCacheConfigType, choose_execution_cache};
26
27#[enum_dispatch]
28pub trait EpochStartConfigTrait {
29    fn epoch_digest(&self) -> CheckpointDigest;
30    fn epoch_start_state(&self) -> &EpochStartSystemState;
31    fn flags(&self) -> &[EpochFlag];
32    fn authenticator_obj_initial_shared_version(&self) -> Option<SequenceNumber>;
33    fn randomness_obj_initial_shared_version(&self) -> SequenceNumber;
34    fn coin_deny_list_obj_initial_shared_version(&self) -> SequenceNumber;
35    fn bridge_obj_initial_shared_version(&self) -> Option<SequenceNumber>;
36    fn bridge_committee_initiated(&self) -> bool;
37
38    fn execution_cache_type(&self) -> ExecutionCacheConfigType {
39        if self.flags().contains(&EpochFlag::WritebackCacheEnabled) {
40            ExecutionCacheConfigType::WritebackCache
41        } else {
42            ExecutionCacheConfigType::PassthroughCache
43        }
44    }
45}
46
47// IMPORTANT: Assign explicit values to each variant to ensure that the values
48// are stable. When cherry-picking changes from one branch to another, the value
49// of variants must never change.
50//
51// Unlikely: If you cherry pick a change from one branch to another, and there
52// is a collision in the value of some variant, the branch which has been
53// released should take precedence. In this case, the picked-from branch is
54// inconsistent with the released branch, and must be fixed.
55#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
56pub enum EpochFlag {
57    WritebackCacheEnabled = 0,
58}
59
60impl EpochFlag {
61    pub fn default_flags_for_new_epoch(config: &NodeConfig) -> Vec<Self> {
62        Self::default_flags_impl(&config.execution_cache)
63    }
64
65    /// For situations in which there is no config available (e.g. setting up a
66    /// downloaded snapshot).
67    pub fn default_for_no_config() -> Vec<Self> {
68        Self::default_flags_impl(&Default::default())
69    }
70
71    fn default_flags_impl(cache_config: &ExecutionCacheConfig) -> Vec<Self> {
72        let mut new_flags = vec![];
73
74        if matches!(
75            choose_execution_cache(cache_config),
76            ExecutionCacheConfigType::WritebackCache
77        ) {
78            new_flags.push(EpochFlag::WritebackCacheEnabled);
79        }
80
81        new_flags
82    }
83}
84
85impl fmt::Display for EpochFlag {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        // Important - implementation should return low cardinality values because this
88        // is used as metric key
89        match self {
90            EpochFlag::WritebackCacheEnabled => write!(f, "WritebackCacheEnabled"),
91        }
92    }
93}
94
95/// Parameters of the epoch fixed at epoch start.
96#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
97#[enum_dispatch(EpochStartConfigTrait)]
98pub enum EpochStartConfiguration {
99    V1(EpochStartConfigurationV1),
100}
101
102impl EpochStartConfiguration {
103    pub fn new(
104        system_state: EpochStartSystemState,
105        epoch_digest: CheckpointDigest,
106        object_store: &dyn ObjectStore,
107        initial_epoch_flags: Vec<EpochFlag>,
108    ) -> IotaResult<Self> {
109        let authenticator_obj_initial_shared_version =
110            get_authenticator_state_obj_initial_shared_version(object_store)?;
111        let randomness_obj_initial_shared_version =
112            get_randomness_state_obj_initial_shared_version(object_store)?;
113        let coin_deny_list_obj_initial_shared_version =
114            get_deny_list_obj_initial_shared_version(object_store)?;
115        let bridge_obj_initial_shared_version =
116            get_bridge_obj_initial_shared_version(object_store)?;
117        let bridge_committee_initiated = is_bridge_committee_initiated(object_store)?;
118        Ok(Self::V1(EpochStartConfigurationV1 {
119            system_state,
120            epoch_digest,
121            flags: initial_epoch_flags,
122            authenticator_obj_initial_shared_version,
123            randomness_obj_initial_shared_version,
124            coin_deny_list_obj_initial_shared_version,
125            bridge_obj_initial_shared_version,
126            bridge_committee_initiated,
127        }))
128    }
129
130    #[expect(unreachable_patterns)]
131    pub fn new_at_next_epoch_for_testing(&self) -> Self {
132        // We only need to implement this function for the latest version.
133        // When a new version is introduced, this function should be updated.
134        match self {
135            Self::V1(config) => Self::V1(EpochStartConfigurationV1 {
136                system_state: config.system_state.new_at_next_epoch_for_testing(),
137                epoch_digest: config.epoch_digest,
138                flags: config.flags.clone(),
139                authenticator_obj_initial_shared_version: config
140                    .authenticator_obj_initial_shared_version,
141                randomness_obj_initial_shared_version: config.randomness_obj_initial_shared_version,
142                coin_deny_list_obj_initial_shared_version: config
143                    .coin_deny_list_obj_initial_shared_version,
144                bridge_obj_initial_shared_version: config.bridge_obj_initial_shared_version,
145                bridge_committee_initiated: config.bridge_committee_initiated,
146            }),
147            _ => panic!(
148                "This function is only implemented for the latest version of EpochStartConfiguration"
149            ),
150        }
151    }
152
153    pub fn epoch_data(&self) -> EpochData {
154        EpochData::new(
155            self.epoch_start_state().epoch(),
156            self.epoch_start_state().epoch_start_timestamp_ms(),
157            self.epoch_digest(),
158        )
159    }
160
161    pub fn epoch_start_timestamp_ms(&self) -> CheckpointTimestamp {
162        self.epoch_start_state().epoch_start_timestamp_ms()
163    }
164}
165
166#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
167pub struct EpochStartConfigurationV1 {
168    system_state: EpochStartSystemState,
169    epoch_digest: CheckpointDigest,
170    flags: Vec<EpochFlag>,
171    /// Do the state objects exist at the beginning of the epoch?
172    authenticator_obj_initial_shared_version: Option<SequenceNumber>,
173    randomness_obj_initial_shared_version: SequenceNumber,
174    coin_deny_list_obj_initial_shared_version: SequenceNumber,
175    bridge_obj_initial_shared_version: Option<SequenceNumber>,
176    bridge_committee_initiated: bool,
177}
178
179impl EpochStartConfigTrait for EpochStartConfigurationV1 {
180    fn epoch_digest(&self) -> CheckpointDigest {
181        self.epoch_digest
182    }
183
184    fn epoch_start_state(&self) -> &EpochStartSystemState {
185        &self.system_state
186    }
187
188    fn flags(&self) -> &[EpochFlag] {
189        &self.flags
190    }
191
192    fn authenticator_obj_initial_shared_version(&self) -> Option<SequenceNumber> {
193        self.authenticator_obj_initial_shared_version
194    }
195
196    fn randomness_obj_initial_shared_version(&self) -> SequenceNumber {
197        self.randomness_obj_initial_shared_version
198    }
199
200    fn coin_deny_list_obj_initial_shared_version(&self) -> SequenceNumber {
201        self.coin_deny_list_obj_initial_shared_version
202    }
203
204    fn bridge_obj_initial_shared_version(&self) -> Option<SequenceNumber> {
205        self.bridge_obj_initial_shared_version
206    }
207
208    fn bridge_committee_initiated(&self) -> bool {
209        self.bridge_committee_initiated
210    }
211}