espresso_types/v0/v0_3/
chain_config.rs

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