Skip to main content

espresso_types/v0/v0_3/
chain_config.rs

1use alloy::primitives::{Address, U256};
2use alloy_compat::ethers_serde;
3use committable::{Commitment, Committable};
4use itertools::Either;
5use serde::{Deserialize, Serialize};
6
7use crate::{BlockSize, ChainId, FeeAccount, FeeAmount, v0_1};
8
9/// Global variables for an Espresso blockchain.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct ChainConfig {
12    /// Espresso chain ID
13    pub chain_id: ChainId,
14
15    /// Maximum size in bytes of a block
16    pub max_block_size: BlockSize,
17
18    /// Minimum fee in WEI per byte of payload
19    pub base_fee: FeeAmount,
20
21    /// Fee contract address on L1.
22    ///
23    /// This is optional so that fees can easily be toggled on/off, with no need to deploy a
24    /// contract when they are off. In a future release, after fees are switched on and thoroughly
25    /// tested, this may be made mandatory.
26    #[serde(with = "ethers_serde::option_address")]
27    #[serde(default)]
28    pub fee_contract: Option<Address>,
29
30    /// Account that receives sequencing fees.
31    ///
32    /// This account in the Espresso fee ledger will always receive every fee paid in Espresso,
33    /// regardless of whether or not their is a `fee_contract` deployed. Once deployed, the fee
34    /// contract can decide what to do with tokens locked in this account in Espresso.
35    pub fee_recipient: FeeAccount,
36
37    /// `StakeTable `(proxy) contract address on L1.
38    ///
39    /// This is optional so that stake can easily be toggled on/off, with no need to deploy a
40    /// contract when they are off. In a future release, after PoS is switched on and thoroughly
41    /// tested, this may be made mandatory.
42    #[serde(with = "ethers_serde::option_address")]
43    #[serde(default)]
44    pub stake_table_contract: Option<Address>,
45}
46
47#[derive(Clone, Debug, Copy, PartialEq, Deserialize, Serialize, Eq, Hash)]
48pub struct ResolvableChainConfig {
49    pub(crate) chain_config: Either<ChainConfig, Commitment<ChainConfig>>,
50}
51
52impl Committable for ChainConfig {
53    fn tag() -> String {
54        "CHAIN_CONFIG".to_string()
55    }
56
57    fn commit(&self) -> Commitment<Self> {
58        let comm = committable::RawCommitmentBuilder::new(&Self::tag())
59            .fixed_size_field("chain_id", &self.chain_id.to_fixed_bytes())
60            .u64_field("max_block_size", *self.max_block_size)
61            .fixed_size_field("base_fee", &self.base_fee.to_fixed_bytes())
62            .fixed_size_field("fee_recipient", &self.fee_recipient.to_fixed_bytes());
63        let comm = if let Some(addr) = self.fee_contract {
64            comm.u64_field("fee_contract", 1).fixed_size_bytes(&addr.0)
65        } else {
66            comm.u64_field("fee_contract", 0)
67        };
68        // With `ChainConfig` upgrades we want commitments w/out
69        // fields added >= v0_3 to have the same commitment as <= v0_3
70        // commitment. Therefore `None` values are simply ignored.
71        let comm = if let Some(addr) = self.stake_table_contract {
72            comm.u64_field("stake_table_contract", 1)
73                .fixed_size_bytes(&addr.0)
74        } else {
75            comm
76        };
77
78        comm.finalize()
79    }
80}
81
82impl ResolvableChainConfig {
83    pub fn commit(&self) -> Commitment<ChainConfig> {
84        match self.chain_config {
85            Either::Left(config) => config.commit(),
86            Either::Right(commitment) => commitment,
87        }
88    }
89    pub fn resolve(self) -> Option<ChainConfig> {
90        match self.chain_config {
91            Either::Left(config) => Some(config),
92            Either::Right(_) => None,
93        }
94    }
95}
96
97impl From<Commitment<ChainConfig>> for ResolvableChainConfig {
98    fn from(value: Commitment<ChainConfig>) -> Self {
99        Self {
100            chain_config: Either::Right(value),
101        }
102    }
103}
104
105impl From<ChainConfig> for ResolvableChainConfig {
106    fn from(value: ChainConfig) -> Self {
107        Self {
108            chain_config: Either::Left(value),
109        }
110    }
111}
112
113impl From<&v0_1::ResolvableChainConfig> for ResolvableChainConfig {
114    fn from(
115        &v0_1::ResolvableChainConfig { chain_config }: &v0_1::ResolvableChainConfig,
116    ) -> ResolvableChainConfig {
117        match chain_config {
118            Either::Left(chain_config) => ResolvableChainConfig {
119                chain_config: Either::Left(ChainConfig::from(chain_config)),
120            },
121            Either::Right(c) => ResolvableChainConfig {
122                chain_config: Either::Right(Commitment::from_raw(*c.as_ref())),
123            },
124        }
125    }
126}
127
128impl From<v0_1::ChainConfig> for ChainConfig {
129    fn from(chain_config: v0_1::ChainConfig) -> ChainConfig {
130        let v0_1::ChainConfig {
131            chain_id,
132            max_block_size,
133            base_fee,
134            fee_contract,
135            fee_recipient,
136            ..
137        } = chain_config;
138
139        ChainConfig {
140            chain_id,
141            max_block_size,
142            base_fee,
143            fee_contract,
144            fee_recipient,
145            stake_table_contract: None,
146        }
147    }
148}
149
150impl From<ChainConfig> for v0_1::ChainConfig {
151    fn from(chain_config: ChainConfig) -> v0_1::ChainConfig {
152        let ChainConfig {
153            chain_id,
154            max_block_size,
155            base_fee,
156            fee_contract,
157            fee_recipient,
158            ..
159        } = chain_config;
160
161        v0_1::ChainConfig {
162            chain_id,
163            max_block_size,
164            base_fee,
165            fee_contract,
166            fee_recipient,
167        }
168    }
169}
170
171impl Default for ChainConfig {
172    fn default() -> Self {
173        Self {
174            chain_id: U256::from(35353).into(), // arbitrarily chosen chain ID
175            max_block_size: 30720.into(),
176            base_fee: 0.into(),
177            fee_contract: None,
178            fee_recipient: Default::default(),
179            stake_table_contract: None,
180        }
181    }
182}
183
184#[cfg(test)]
185mod test {
186    use super::*;
187
188    #[test]
189    fn test_upgrade_chain_config_v3_resolvable_chain_config_from_v1() {
190        let expectation: ResolvableChainConfig = ChainConfig::default().into();
191        let v1_resolvable: v0_1::ResolvableChainConfig = v0_1::ChainConfig::default().into();
192        let v3_resolvable: ResolvableChainConfig = ResolvableChainConfig::from(&v1_resolvable);
193        assert_eq!(expectation, v3_resolvable);
194        let expectation: ResolvableChainConfig = ChainConfig::default().commit().into();
195        let v1_resolvable: v0_1::ResolvableChainConfig =
196            v0_1::ChainConfig::default().commit().into();
197        let v3_resolvable: ResolvableChainConfig = ResolvableChainConfig::from(&v1_resolvable);
198        assert_eq!(expectation, v3_resolvable);
199    }
200
201    #[test]
202    fn test_upgrade_chain_config_v1_chain_config_from_v3() {
203        let expectation = v0_1::ChainConfig::default();
204        let v3_chain_config = ChainConfig::default();
205        let v1_chain_config = v0_1::ChainConfig::from(v3_chain_config);
206        assert_eq!(expectation, v1_chain_config);
207    }
208}