ethrex_common/
genesis_utils.rs1use crate::types::Genesis;
2use serde_json::{Map, Value};
3use std::path::Path;
4
5fn sort_config(genesis_map: &Map<String, Value>) -> Result<Map<String, Value>, String> {
6 let config_keys_order = [
7 "chainId",
8 "homesteadBlock",
9 "daoForkBlock",
10 "daoForkSupport",
11 "eip150Block",
12 "eip150Hash",
13 "eip155Block",
14 "eip158Block",
15 "byzantiumBlock",
16 "constantinopleBlock",
17 "petersburgBlock",
18 "istanbulBlock",
19 "muirGlacierBlock",
20 "berlinBlock",
21 "londonBlock",
22 "arrowGlacierBlock",
23 "grayGlacierBlock",
24 "terminalTotalDifficulty",
25 "terminalTotalDifficultyPassed",
26 "shanghaiTime",
27 "cancunTime",
28 "pragueTime",
29 "osakaTime",
30 "bpo1Time",
31 "bpo2Time",
32 "bpo3Time",
33 "bpo4Time",
34 "bpo5Time",
35 "amsterdamTime",
36 "hegotaTime",
37 "lstarTime",
38 "verkleTime",
39 "ethash",
40 "depositContractAddress",
41 "blobSchedule",
42 "mergeNetsplitBlock",
43 "enableVerkleAtGenesis",
44 ];
45 let Value::Object(config) = genesis_map
46 .get("config")
47 .ok_or_else(|| "Genesis file is missing config".to_owned())?
48 else {
49 return Err("Genesis file config is not a json object".to_owned());
50 };
51 let mut ordered_config: Map<String, Value> = Map::new();
52 for key in config_keys_order {
53 if let Some(value) = config.get(key)
58 && *value != Value::Null
59 {
60 ordered_config.insert(key.to_owned(), value.clone());
61 };
62 }
63 for key in config.keys() {
65 if ordered_config.get(key).is_none() && config.get(key) != Some(&Value::Null) {
66 return Err(format!("Missing key in sorted config: {key}"));
67 }
68 }
69 Ok(ordered_config)
70}
71
72pub fn write_genesis_as_json(genesis: Genesis, path: &Path) -> Result<(), String> {
73 let genesis_json = serde_json::to_string(&genesis)
74 .map_err(|e| format!("Could not convert genesis to string: {e}"))?;
75 let genesis_as_map: Map<String, Value> = serde_json::from_str(&genesis_json)
76 .map_err(|e| format!("Failed to de-serialize genesis file: {e}"))?;
77 let keys = [
82 "nonce",
83 "timestamp",
84 "extraData",
85 "gasLimit",
86 "difficulty",
87 "mixHash",
88 "coinbase",
89 "alloc",
90 ];
91 let ordered_config = sort_config(&genesis_as_map)?;
92 let optional_keys = [
96 "number",
97 "gasUsed",
98 "parentHash",
99 "baseFeePerGas",
100 "excessBlobGas",
101 "requestsHash",
102 "blobGasUsed",
103 ];
104 let mut ordered_map: Map<String, Value> = serde_json::Map::new();
107 ordered_map.insert(
108 "config".to_owned(),
109 serde_json::Value::Object(ordered_config),
110 );
111 for k in keys {
112 let Some(v) = genesis_as_map.get(k) else {
113 return Err(format!("Missing key in read genesis file: {k}"));
114 };
115 if *v != Value::Null {
116 ordered_map.insert(k.to_owned(), v.clone());
117 }
118 }
119 for k in optional_keys {
120 if let Some(v) = genesis_as_map.get(k)
121 && *v != Value::Null
122 {
123 ordered_map.insert(k.to_owned(), v.clone().take());
124 }
125 }
126 for k in genesis_as_map.keys() {
128 let expected = genesis_as_map.get(k);
129 if expected != Some(&Value::Null)
130 && ordered_map.contains_key(k) != genesis_as_map.contains_key(k)
131 {
132 return Err(format!("Genesis serialization is missing a key: {k}"));
133 }
134 }
135 let _: Genesis = serde_json::from_value(ordered_map.clone().into())
137 .map_err(|e| format!("Error turning into genesis: {e}"))?;
138 let to_write = serde_json::to_string_pretty(&ordered_map)
139 .map_err(|e| format!("Could not turn map into json: {e}"))?;
140 std::fs::write(path, &to_write).map_err(|e| {
141 format!(
142 "Could not write genesis json to path: {}, error: {e}",
143 path.display()
144 )
145 })
146}