tempo_xtask/
shadowfork.rs1use std::{
2 ffi::OsStr,
3 path::{Path, PathBuf},
4};
5
6use eyre::{OptionExt as _, WrapErr as _, eyre};
7
8pub(crate) const SHADOWFORK_SIGNING_KEY_SECRET: &str = "tempo-shadowfork-signing-key-secret";
9pub(crate) const SHADOW_CHAINSPEC_FILE: &str = "shadowfork-chain.json";
10pub(crate) const SHADOW_EPOCH: u64 = 1;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub(crate) struct SourceExecutionDataDir {
14 pub(crate) db_path: PathBuf,
15 pub(crate) static_files_path: PathBuf,
16}
17
18pub(crate) fn source_chain_cli_arg(
19 source_chain: &str,
20 source_chain_id: u64,
21) -> Option<&'static str> {
22 match source_chain.to_ascii_lowercase().as_str() {
23 "mainnet" | "presto" => Some("mainnet"),
24 "moderato" | "testnet" => Some("moderato"),
25 "dev" => Some("dev"),
26 _ if source_chain_id == 4217 => Some("mainnet"),
27 _ if source_chain_id == 42431 => Some("moderato"),
28 _ => None,
29 }
30}
31
32pub(crate) fn source_chain_id(source_chain: &str) -> Option<u64> {
33 match source_chain.to_ascii_lowercase().as_str() {
34 "mainnet" | "presto" => Some(4217),
35 "moderato" | "testnet" => Some(42431),
36 "dev" => Some(1337),
37 _ => None,
38 }
39}
40
41pub(crate) fn resolve_source_execution_data_dir(
42 path: &Path,
43 source_chain: &str,
44 source_chain_id: u64,
45) -> eyre::Result<SourceExecutionDataDir> {
46 let mut candidates = Vec::new();
47 if path.file_name() == Some(OsStr::new("db")) {
48 candidates.push(path.to_path_buf());
49 }
50 candidates.push(path.join("db"));
51 if let Some(chain_dir) = source_chain_cli_arg(source_chain, source_chain_id) {
52 candidates.push(path.join(chain_dir).join("db"));
53 }
54 candidates.push(path.join(source_chain_id.to_string()).join("db"));
55
56 for db_path in &candidates {
57 if db_path.exists() {
58 let static_files_path = db_path
59 .parent()
60 .ok_or_else(|| {
61 eyre!(
62 "execution database path `{}` has no parent directory",
63 db_path.display(),
64 )
65 })?
66 .join("static_files");
67 return Ok(SourceExecutionDataDir {
68 db_path: db_path.clone(),
69 static_files_path,
70 });
71 }
72 }
73
74 let candidates = candidates
75 .iter()
76 .map(|path| format!("`{}`", path.display()))
77 .collect::<Vec<_>>()
78 .join(", ");
79 Err(eyre!(
80 "could not find execution database under `{}`; looked for {candidates}",
81 path.display(),
82 ))
83}
84
85pub(crate) fn write_shadow_chainspec(
86 path: &Path,
87 source_chain: &str,
88 source_chain_id: u64,
89 shadow_epoch_length: u64,
90) -> eyre::Result<()> {
91 let mut genesis = source_genesis_json(source_chain, source_chain_id)?;
92 let config = genesis
93 .get_mut("config")
94 .and_then(serde_json::Value::as_object_mut)
95 .ok_or_eyre("source genesis JSON does not contain an object at `config`")?;
96 config.insert(
97 "epochLength".to_string(),
98 serde_json::Value::from(shadow_epoch_length),
99 );
100
101 let json = serde_json::to_string_pretty(&genesis)
102 .wrap_err("failed serializing shadow chainspec JSON")?;
103 std::fs::write(path, json)
104 .wrap_err_with(|| format!("failed writing shadow chainspec to `{}`", path.display()))
105}
106
107fn source_genesis_json(
108 source_chain: &str,
109 source_chain_id: u64,
110) -> eyre::Result<serde_json::Value> {
111 let genesis = match source_chain.to_ascii_lowercase().as_str() {
112 "mainnet" | "presto" => include_str!("../../crates/chainspec/src/genesis/presto.json"),
113 "moderato" | "testnet" => include_str!("../../crates/chainspec/src/genesis/moderato.json"),
114 "dev" => include_str!("../../crates/chainspec/src/genesis/dev.json"),
115 _ if source_chain_id == 4217 => {
116 include_str!("../../crates/chainspec/src/genesis/presto.json")
117 }
118 _ if source_chain_id == 42431 => {
119 include_str!("../../crates/chainspec/src/genesis/moderato.json")
120 }
121 _ => {
122 return Err(eyre!(
123 "cannot infer source chainspec for source_chain `{source_chain}` and chain id `{source_chain_id}`"
124 ));
125 }
126 };
127
128 serde_json::from_str(genesis).wrap_err("failed parsing bundled source genesis JSON")
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn maps_source_chain_to_execution_chain() {
137 assert_eq!(source_chain_cli_arg("mainnet", 0), Some("mainnet"));
138 assert_eq!(source_chain_cli_arg("presto", 0), Some("mainnet"));
139 assert_eq!(source_chain_cli_arg("moderato", 0), Some("moderato"));
140 assert_eq!(source_chain_cli_arg("testnet", 0), Some("moderato"));
141 assert_eq!(source_chain_cli_arg("dev", 0), Some("dev"));
142 assert_eq!(source_chain_cli_arg("custom", 4217), Some("mainnet"));
143 assert_eq!(source_chain_cli_arg("custom", 42431), Some("moderato"));
144 assert_eq!(source_chain_cli_arg("custom", 1), None);
145 }
146
147 #[test]
148 fn maps_source_chain_to_chain_id() {
149 assert_eq!(source_chain_id("mainnet"), Some(4217));
150 assert_eq!(source_chain_id("presto"), Some(4217));
151 assert_eq!(source_chain_id("moderato"), Some(42431));
152 assert_eq!(source_chain_id("testnet"), Some(42431));
153 assert_eq!(source_chain_id("dev"), Some(1337));
154 assert_eq!(source_chain_id("custom"), None);
155 }
156}