reth_optimism_chainspec/superchain/
configs.rs1use crate::superchain::chain_metadata::{to_genesis_chain_config, ChainMetadata};
2use alloc::{
3 format,
4 string::{String, ToString},
5 vec::Vec,
6};
7use alloy_genesis::Genesis;
8use miniz_oxide::inflate::decompress_to_vec_zlib_with_limit;
9use tar_no_std::{CorruptDataError, TarArchiveRef};
10
11const MAX_GENESIS_SIZE: usize = 16 * 1024 * 1024; const SUPER_CHAIN_CONFIGS_TAR_BYTES: &[u8] = include_bytes!("../../res/superchain-configs.tar");
16
17#[derive(Debug, thiserror::Error)]
18pub(crate) enum SuperchainConfigError {
19 #[error("Error reading archive due to corrupt data: {0}")]
20 CorruptDataError(CorruptDataError),
21 #[error("Error converting bytes to UTF-8 String: {0}")]
22 FromUtf8Error(#[from] alloc::string::FromUtf8Error),
23 #[error("Error reading file: {0}")]
24 Utf8Error(#[from] core::str::Utf8Error),
25 #[error("Error deserializing JSON: {0}")]
26 JsonError(#[from] serde_json::Error),
27 #[error("File {0} not found in archive")]
28 FileNotFound(String),
29 #[error("Error decompressing file: {0}")]
30 DecompressError(String),
31}
32
33pub(crate) fn read_superchain_genesis(
36 name: &str,
37 environment: &str,
38) -> Result<Genesis, SuperchainConfigError> {
39 let archive = TarArchiveRef::new(SUPER_CHAIN_CONFIGS_TAR_BYTES)
41 .map_err(SuperchainConfigError::CorruptDataError)?;
42 let compressed_genesis_file =
44 read_file(&archive, &format!("genesis/{environment}/{name}.json.zz"))?;
45 let genesis_file =
46 decompress_to_vec_zlib_with_limit(&compressed_genesis_file, MAX_GENESIS_SIZE)
47 .map_err(|e| SuperchainConfigError::DecompressError(format!("{e}")))?;
48
49 let mut genesis: Genesis = serde_json::from_slice(&genesis_file)?;
51
52 genesis.config =
56 to_genesis_chain_config(&read_superchain_metadata(name, environment, &archive)?);
57
58 Ok(genesis)
59}
60
61fn read_superchain_metadata(
64 name: &str,
65 environment: &str,
66 archive: &TarArchiveRef<'_>,
67) -> Result<ChainMetadata, SuperchainConfigError> {
68 let config_file = read_file(archive, &format!("configs/{environment}/{name}.json"))?;
69 let config_content = String::from_utf8(config_file)?;
70 let chain_config: ChainMetadata = serde_json::from_str(&config_content)?;
71 Ok(chain_config)
72}
73
74fn read_file(
76 archive: &TarArchiveRef<'_>,
77 file_path: &str,
78) -> Result<Vec<u8>, SuperchainConfigError> {
79 for entry in archive.entries() {
80 if entry.filename().as_str()? == file_path {
81 return Ok(entry.data().to_vec())
82 }
83 }
84 Err(SuperchainConfigError::FileNotFound(file_path.to_string()))
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::superchain::Superchain;
91 use reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER;
92 use tar_no_std::TarArchiveRef;
93
94 #[test]
95 fn test_read_superchain_genesis() {
96 let genesis = read_superchain_genesis("unichain", "mainnet").unwrap();
97 assert_eq!(genesis.config.chain_id, 130);
98 assert_eq!(genesis.timestamp, 1730748359);
99 assert!(genesis.alloc.contains_key(&ADDRESS_L2_TO_L1_MESSAGE_PASSER));
100 }
101
102 #[test]
103 fn test_read_superchain_genesis_with_workaround() {
104 let genesis = read_superchain_genesis("funki", "mainnet").unwrap();
105 assert_eq!(genesis.config.chain_id, 33979);
106 assert_eq!(genesis.timestamp, 1721211095);
107 assert!(genesis.alloc.contains_key(&ADDRESS_L2_TO_L1_MESSAGE_PASSER));
108 }
109
110 #[test]
111 fn test_read_superchain_metadata() {
112 let archive = TarArchiveRef::new(SUPER_CHAIN_CONFIGS_TAR_BYTES).unwrap();
113 let chain_config = read_superchain_metadata("funki", "mainnet", &archive).unwrap();
114 assert_eq!(chain_config.chain_id, 33979);
115 }
116
117 #[test]
118 fn test_read_all_genesis_files() {
119 let archive = TarArchiveRef::new(SUPER_CHAIN_CONFIGS_TAR_BYTES).unwrap();
120 for entry in archive.entries() {
122 let filename = entry
123 .filename()
124 .as_str()
125 .unwrap()
126 .split('/')
127 .map(|s| s.to_string())
128 .collect::<Vec<String>>();
129 if filename.first().unwrap().ne(&"genesis") {
130 continue
131 }
132 read_superchain_metadata(
133 &filename.get(2).unwrap().replace(".json.zz", ""),
134 filename.get(1).unwrap(),
135 &archive,
136 )
137 .unwrap();
138 }
139 }
140
141 #[test]
142 fn test_genesis_exists_for_all_available_chains() {
143 for &chain in Superchain::ALL {
144 let genesis = read_superchain_genesis(chain.name(), chain.environment());
145 assert!(
146 genesis.is_ok(),
147 "Genesis not found for chain: {}-{}",
148 chain.name(),
149 chain.environment()
150 );
151 }
152 }
153}