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 "verkleTime",
31 "ethash",
32 "depositContractAddress",
33 "blobSchedule",
34 "mergeNetsplitBlock",
35 "enableVerkleAtGenesis",
36 ];
37 let Value::Object(config) = genesis_map
38 .get("config")
39 .ok_or_else(|| "Genesis file is missing config".to_owned())?
40 else {
41 return Err("Genesis file config is not a json object".to_owned());
42 };
43 let mut ordered_config: Map<String, Value> = Map::new();
44 for key in config_keys_order {
45 if let Some(value) = config.get(key)
50 && *value != Value::Null
51 {
52 ordered_config.insert(key.to_owned(), value.clone());
53 };
54 }
55 for key in config.keys() {
57 if ordered_config.get(key).is_none() && config.get(key) != Some(&Value::Null) {
58 return Err(format!("Missing key in sorted config: {key}"));
59 }
60 }
61 Ok(ordered_config)
62}
63
64pub fn write_genesis_as_json(genesis: Genesis, path: &Path) -> Result<(), String> {
65 let genesis_json = serde_json::to_string(&genesis)
66 .map_err(|e| format!("Could not convert genesis to string: {e}"))?;
67 let genesis_as_map: Map<String, Value> = serde_json::from_str(&genesis_json)
68 .map_err(|e| format!("Failed to de-serialize genesis file: {e}"))?;
69 let keys = [
74 "nonce",
75 "timestamp",
76 "extraData",
77 "gasLimit",
78 "difficulty",
79 "mixHash",
80 "coinbase",
81 "alloc",
82 ];
83 let ordered_config = sort_config(&genesis_as_map)?;
84 let optional_keys = [
88 "number",
89 "gasUsed",
90 "parentHash",
91 "baseFeePerGas",
92 "excessBlobGas",
93 "requestsHash",
94 "blobGasUsed",
95 ];
96 let mut ordered_map: Map<String, Value> = serde_json::Map::new();
99 ordered_map.insert(
100 "config".to_owned(),
101 serde_json::Value::Object(ordered_config),
102 );
103 for k in keys {
104 let Some(v) = genesis_as_map.get(k) else {
105 return Err(format!("Missing key in read genesis file: {k}"));
106 };
107 if *v != Value::Null {
108 ordered_map.insert(k.to_owned(), v.clone());
109 }
110 }
111 for k in optional_keys {
112 if let Some(v) = genesis_as_map.get(k)
113 && *v != Value::Null
114 {
115 ordered_map.insert(k.to_owned(), v.clone().take());
116 }
117 }
118 for k in genesis_as_map.keys() {
120 let expected = genesis_as_map.get(k);
121 if expected != Some(&Value::Null)
122 && ordered_map.contains_key(k) != genesis_as_map.contains_key(k)
123 {
124 return Err(format!("Genesis serialization is missing a key: {k}"));
125 }
126 }
127 let _: Genesis = serde_json::from_value(ordered_map.clone().into())
129 .map_err(|e| format!("Error turning into genesis: {e}"))?;
130 let to_write = serde_json::to_string_pretty(&ordered_map)
131 .map_err(|e| format!("Could not turn map into json: {e}"))?;
132 std::fs::write(path, &to_write).map_err(|e| {
133 format!(
134 "Could not write genesis json to path: {}, error: {e}",
135 path.display()
136 )
137 })
138}