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