1#![allow(non_snake_case)]
2
3mod net_overrides;
4use std::path::{Path, PathBuf};
5use std::str::FromStr as _;
6pub trait ToEnvVar {
7 fn to_env_value(&self) -> Option<String> {
8 panic!("Must implement one of the two ToEnvVar methods");
9 }
10
11 fn to_env_values(&self, base: &str) -> impl Iterator<Item = (String, String)> {
12 self.to_env_value()
13 .into_iter()
14 .map(|v| (base.to_owned(), v))
15 }
16}
17
18macro_rules! declare_vars {
19 ($struct:ident = ($($args:tt)*) =>
20 {
21 $($name:ident : $ty:ty = $value:expr_2021; env: $env:expr_2021;)*
22 }
23 ) => {
24 #[derive(Clone, Debug)]
25 pub struct $struct {
26 $(
27 #[allow(unused)]
28 pub $name: $ty
29 ),*
30 }
31
32 impl $struct {
33 pub async fn init($($args)*) -> ::anyhow::Result<Self> {
34 $(let $name: $ty = $value.into();)*
35 Ok(Self {
36 $($name),*
37 })
38 }
39
40 pub fn vars(&self) -> impl Iterator<Item = (String, String)> + use<> {
41 let mut env = ::std::vec::Vec::new();
42 $(
43 for (env_name, value) in $crate::vars::ToEnvVar::to_env_values(&self.$name, $env) {
44 env.push((env_name, value));
45 }
46 )*
47 env.into_iter()
48 }
49 }
50 };
51}
52
53impl ToEnvVar for PathBuf {
54 fn to_env_value(&self) -> Option<String> {
55 Some(self.as_os_str().to_str().expect("must be utf8").to_owned())
56 }
57}
58
59impl ToEnvVar for String {
60 fn to_env_value(&self) -> Option<String> {
61 Some(self.to_owned())
62 }
63}
64
65impl ToEnvVar for usize {
66 fn to_env_value(&self) -> Option<String> {
67 Some(self.to_string())
68 }
69}
70
71impl ToEnvVar for u16 {
72 fn to_env_value(&self) -> Option<String> {
73 Some(self.to_string())
74 }
75}
76
77impl<T: ToEnvVar> ToEnvVar for Option<T> {
78 fn to_env_value(&self) -> Option<String> {
79 self.as_ref().and_then(ToEnvVar::to_env_value)
80 }
81}
82
83impl ToEnvVar for ApiSecrets {
84 fn to_env_value(&self) -> Option<String> {
85 if self.is_empty() {
86 return None;
87 }
88 Some(self.get_all().join(","))
89 }
90}
91
92pub async fn mkdir(dir: PathBuf) -> anyhow::Result<PathBuf> {
93 if !dir.exists() {
94 tokio::fs::create_dir(&dir).await?;
95 }
96 Ok(dir)
97}
98
99use fedimint_core::envs::{
100 FM_DEFAULT_BITCOIN_RPC_KIND_ENV, FM_DEFAULT_BITCOIN_RPC_URL_ENV, FM_FORCE_BITCOIN_RPC_KIND_ENV,
101 FM_FORCE_BITCOIN_RPC_URL_ENV, FM_IN_DEVIMINT_ENV, FM_IROH_API_SECRET_KEY_OVERRIDE_ENV,
102 FM_IROH_P2P_SECRET_KEY_OVERRIDE_ENV, FM_USE_UNKNOWN_MODULE_ENV,
103};
104use fedimint_core::{NumPeers, PeerId};
105use fedimint_portalloc::port_alloc;
106use fedimint_server::net::api::ApiSecrets;
107use fedimintd::envs::FM_FORCE_API_SECRETS_ENV;
108use format as f;
109use net_overrides::{FederationsNetOverrides, FedimintdPeerOverrides};
110
111use crate::federation::{FEDIMINTD_METRICS_PORT_OFFSET, PORTS_PER_FEDIMINTD};
112
113pub fn utf8(path: &Path) -> &str {
114 path.as_os_str().to_str().expect("must be valid utf8")
115}
116
117declare_vars! {
118 Global = (test_dir: &Path, num_feds: usize, fed_size: usize, offline_nodes: usize) =>
119 {
120 FM_USE_UNKNOWN_MODULE: String = std::env::var(FM_USE_UNKNOWN_MODULE_ENV).unwrap_or_else(|_| "1".into()); env: "FM_USE_UNKNOWN_MODULE";
121
122 FM_FORCE_API_SECRETS: ApiSecrets = std::env::var(FM_FORCE_API_SECRETS_ENV).ok().and_then(|s| {
123 ApiSecrets::from_str(&s).ok()
124 }).unwrap_or_default(); env: FM_FORCE_API_SECRETS_ENV;
125
126 FM_API_SECRET: Option<String> = std::env::var("FM_API_SECRET").ok().or_else(|| FM_FORCE_API_SECRETS.get_active()); env: "FM_API_SECRET";
127
128 FM_IN_DEVIMINT: String = "1".to_string(); env: FM_IN_DEVIMINT_ENV;
129 FM_SKIP_REL_NOTES_ACK: String = "1".to_string(); env: "FM_SKIP_REL_NOTES_ACK";
130
131 FM_FED_SIZE: usize = fed_size; env: "FM_FED_SIZE";
132 FM_NUM_FEDS: usize = num_feds; env: "FM_NUM_FEDS";
133 FM_OFFLINE_NODES: usize = offline_nodes; env: "FM_OFFLINE_NODES";
134 FM_TMP_DIR: PathBuf = mkdir(test_dir.into()).await?; env: "FM_TMP_DIR";
135 FM_TEST_DIR: PathBuf = FM_TMP_DIR.clone(); env: "FM_TEST_DIR";
136 FM_TEST_FAST_WEAK_CRYPTO: String = "1"; env: "FM_TEST_FAST_WEAK_CRYPTO";
137 FM_LOGS_DIR: PathBuf = mkdir(FM_TEST_DIR.join("logs")).await?; env: "FM_LOGS_DIR";
138
139 FM_PORT_BTC_RPC: u16 = port_alloc(1)?; env: "FM_PORT_BTC_RPC";
140 FM_PORT_BTC_P2P: u16 = port_alloc(1)?; env: "FM_PORT_BTC_P2P";
141 FM_PORT_BTC_ZMQ_PUB_RAW_BLOCK: u16 = port_alloc(1)?; env: "FM_PORT_BTC_ZMQ_PUB_RAW_BLOCK";
142 FM_PORT_BTC_ZMQ_PUB_RAW_TX: u16 = port_alloc(1)?; env: "FM_PORT_BTC_ZMQ_PUB_RAW_TX";
143 FM_PORT_CLN: u16 = port_alloc(1)?; env: "FM_PORT_CLN";
144 FM_PORT_LND_LISTEN: u16 = port_alloc(1)?; env: "FM_PORT_LND_LISTEN";
145 FM_PORT_LDK: u16 = port_alloc(1)?; env: "FM_PORT_LDK";
146 FM_PORT_LND_RPC: u16 = port_alloc(1)?; env: "FM_PORT_LND_RPC";
147 FM_PORT_LND_REST: u16 = port_alloc(1)?; env: "FM_PORT_LND_REST";
148 FM_PORT_ELECTRS: u16 = port_alloc(1)?; env: "FM_PORT_ELECTRS";
149 FM_PORT_ELECTRS_MONITORING: u16 = port_alloc(1)?; env: "FM_PORT_ELECTRS_MONITORING";
150 FM_PORT_ESPLORA: u16 = port_alloc(1)?; env: "FM_PORT_ESPLORA";
151 FM_PORT_ESPLORA_MONITORING: u16 = port_alloc(1)?; env: "FM_PORT_ESPLORA_MONITORING";
152 FM_PORT_FEDIMINTD_BASE: u16 = port_alloc((3 * fed_size).try_into().unwrap())?; env: "FM_PORT_FEDIMINTD_BASE";
154 FM_PORT_GW_LND: u16 = port_alloc(1)?; env: "FM_PORT_GW_LND";
155 FM_PORT_GW_LDK: u16 = port_alloc(1)?; env: "FM_PORT_GW_LDK";
156 FM_PORT_FAUCET: u16 = 15243u16; env: "FM_PORT_FAUCET";
157
158 FM_FEDERATION_BASE_PORT: u16 = port_alloc((PORTS_PER_FEDIMINTD as usize * fed_size * num_feds).try_into().unwrap())?; env: "FM_FEDERATION_BASE_PORT";
159 fedimintd_overrides: FederationsNetOverrides = FederationsNetOverrides::new(FM_FEDERATION_BASE_PORT, num_feds, NumPeers::from(fed_size)); env: "NOT_USED_FOR_ANYTHING";
160
161 FM_LDK_BITCOIND_RPC_URL: String = format!("http://bitcoin:bitcoin@127.0.0.1:{FM_PORT_BTC_RPC}"); env: "FM_LDK_BITCOIND_RPC_URL";
162
163 FM_CLN_DIR: PathBuf = mkdir(FM_TEST_DIR.join("cln")).await?; env: "FM_CLN_DIR";
164 FM_LND_DIR: PathBuf = mkdir(FM_TEST_DIR.join("lnd")).await?; env: "FM_LND_DIR";
165 FM_LDK_DIR: PathBuf = mkdir(FM_TEST_DIR.join("ldk")).await?; env: "FM_LDK_DIR";
166 FM_BTC_DIR: PathBuf = mkdir(FM_TEST_DIR.join("bitcoin")).await?; env: "FM_BTC_DIR";
167 FM_DATA_DIR: PathBuf = FM_TEST_DIR.clone(); env: "FM_DATA_DIR";
168 FM_CLIENT_BASE_DIR: PathBuf = mkdir(FM_TEST_DIR.join("clients")).await?; env: "FM_CLIENT_BASE_DIR";
169 FM_CLIENT_DIR: PathBuf = mkdir(FM_TEST_DIR.join("clients").join("default-0")).await?; env: "FM_CLIENT_DIR";
170 FM_ELECTRS_DIR: PathBuf = mkdir(FM_TEST_DIR.join("electrs")).await?; env: "FM_ELECTRS_DIR";
171 FM_ESPLORA_DIR: PathBuf = mkdir(FM_TEST_DIR.join("esplora")).await?; env: "FM_ESPLORA_DIR";
172 FM_READY_FILE: PathBuf = FM_TEST_DIR.join("ready"); env: "FM_READY_FILE";
173
174 FM_CLN_SOCKET: PathBuf = FM_CLN_DIR.join("regtest/lightning-rpc"); env: "FM_CLN_SOCKET";
175 FM_LND_RPC_ADDR: String = f!("https://localhost:{FM_PORT_LND_RPC}"); env: "FM_LND_RPC_ADDR";
176 FM_LND_TLS_CERT: PathBuf = FM_LND_DIR.join("tls.cert"); env: "FM_LND_TLS_CERT";
177 FM_LND_MACAROON: PathBuf = FM_LND_DIR.join("data/chain/bitcoin/regtest/admin.macaroon"); env: "FM_LND_MACAROON";
178
179 FM_GATEWAY_PASSWORD: String = "theresnosecondbest"; env: "FM_GATEWAY_PASSWORD";
181
182 FM_GATEWAY_BCRYPT_PASSWORD_HASH: String = "$2y$10$Q/UTDeO84VGG1mRncxw.Nubqyi/HsNRJ40k0TSexFy9eVess1yi/u"; env: "FM_GATEWAY_BCRYPT_PASSWORD_HASH";
184
185 FM_GATEWAY_SKIP_WAIT_FOR_SYNC: String = "1"; env: "FM_GATEWAY_SKIP_WAIT_FOR_SYNC";
186 FM_GATEWAY_NETWORK: String = "regtest"; env: "FM_GATEWAY_NETWORK";
187 FM_GATEWAY_LIGHTNING_MODULE_MODE: String = "All"; env: "FM_GATEWAY_LIGHTNING_MODULE_MODE";
188
189 FM_FAUCET_BIND_ADDR: String = f!("0.0.0.0:{FM_PORT_FAUCET}"); env: "FM_FAUCET_BIND_ADDR";
190
191 FM_LIGHTNING_CLI: String = f!("{lightning_cli} --network regtest --lightning-dir={lightning_dir}",
193 lightning_cli = crate::util::get_lightning_cli_path().join(" "),
194 lightning_dir = utf8(&FM_CLN_DIR)); env: "FM_LIGHTNING_CLI";
195 FM_LNCLI: String = f!("{lncli} -n regtest --lnddir={lnddir} --rpcserver=localhost:{FM_PORT_LND_RPC}",
196 lncli = crate::util::get_lncli_path().join(" "),
197 lnddir = utf8(&FM_LND_DIR)); env: "FM_LNCLI";
198 FM_BTC_CLIENT: String = f!("{bitcoin_cli} -regtest -rpcuser=bitcoin -rpcpassword=bitcoin -datadir={datadir}", bitcoin_cli = crate::util::get_bitcoin_cli_path().join(" "),
199 datadir = utf8(&FM_BTC_DIR)); env: "FM_BTC_CLIENT";
200
201 FM_MINT_CLIENT: String = f!("{fedimint_cli} --data-dir {datadir}",
202 fedimint_cli = crate::util::get_fedimint_cli_path().join(" "),
203 datadir = utf8(&FM_CLIENT_DIR)); env: "FM_MINT_CLIENT";
204 FM_MINT_RPC_CLIENT: String = f!("mint-rpc-client"); env: "FM_MINT_RPC_CLIENT";
205 FM_GWCLI_LND: String = f!("{gateway_cli} --rpcpassword=theresnosecondbest -a http://127.0.0.1:{FM_PORT_GW_LND}/",
206 gateway_cli = crate::util::get_gateway_cli_path().join(" "),); env: "FM_GWCLI_LND";
207 FM_GWCLI_LDK: String = f!("{gateway_cli} --rpcpassword=theresnosecondbest -a http://127.0.0.1:{FM_PORT_GW_LDK}/",
208 gateway_cli = crate::util::get_gateway_cli_path().join(" "),); env: "FM_GWCLI_LDK";
209 FM_DB_TOOL: String = f!("{fedimint_dbtool}", fedimint_dbtool = crate::util::get_fedimint_dbtool_cli_path().join(" ")); env: "FM_DB_TOOL";
210
211 FM_TEST_BITCOIND_RPC: String = f!("http://bitcoin:bitcoin@127.0.0.1:{FM_PORT_BTC_RPC}"); env: "FM_TEST_BITCOIND_RPC";
213 FM_BITCOIN_RPC_URL: String = f!("http://bitcoin:bitcoin@127.0.0.1:{FM_PORT_BTC_RPC}"); env: "FM_BITCOIN_RPC_URL";
214 FM_BITCOIN_RPC_KIND: String = "bitcoind"; env: "FM_BITCOIN_RPC_KIND";
215 FM_DEFAULT_BITCOIN_RPC_URL: String = f!("http://bitcoin:bitcoin@127.0.0.1:{FM_PORT_BTC_RPC}"); env: FM_DEFAULT_BITCOIN_RPC_URL_ENV;
216 FM_DEFAULT_BITCOIN_RPC_KIND: String = "bitcoind"; env: FM_DEFAULT_BITCOIN_RPC_KIND_ENV;
217
218 FM_ROCKSDB_WRITE_BUFFER_SIZE : String = (1 << 20).to_string(); env: "FM_ROCKSDB_WRITE_BUFFER_SIZE";
219 }
220}
221
222impl Global {
223 pub async fn new(
224 test_dir: &Path,
225 num_feds: usize,
226 fed_size: usize,
227 offline_nodes: usize,
228 ) -> anyhow::Result<Self> {
229 let this = Self::init(test_dir, num_feds, fed_size, offline_nodes).await?;
230 Ok(this)
231 }
232}
233
234declare_vars! {
235 Fedimintd = (globals: &Global, federation_name: String, peer_id: PeerId, overrides: &FedimintdPeerOverrides) => {
236 FM_BIND_P2P: String = format!("127.0.0.1:{}", overrides.p2p.port()); env: "FM_BIND_P2P";
237 FM_BIND_API: String = format!("127.0.0.1:{}", overrides.api.port()); env: "FM_BIND_API";
238 FM_P2P_URL: String = format!("fedimint://127.0.0.1:{}", overrides.p2p.port()); env: "FM_P2P_URL";
239 FM_API_URL: String = format!("ws://127.0.0.1:{}", overrides.api.port()); env: "FM_API_URL";
240 FM_BIND_METRICS_API: String = format!("127.0.0.1:{}", overrides.base_port + FEDIMINTD_METRICS_PORT_OFFSET); env: "FM_BIND_METRICS_API";
241 FM_DATA_DIR: PathBuf = mkdir(globals.FM_DATA_DIR.join(format!("fedimintd-{federation_name}-{peer_id}"))).await?; env: "FM_DATA_DIR";
242
243 FM_IROH_P2P_SECRET_KEY_OVERRIDE : String = overrides.p2p.secret_key(); env: FM_IROH_P2P_SECRET_KEY_OVERRIDE_ENV;
244 FM_IROH_API_SECRET_KEY_OVERRIDE : String = overrides.api.secret_key(); env: FM_IROH_API_SECRET_KEY_OVERRIDE_ENV;
245
246 FM_FORCE_BITCOIN_RPC_URL: String = f!("http://bitcoin:bitcoin@127.0.0.1:{}", globals.FM_PORT_BTC_RPC); env: FM_FORCE_BITCOIN_RPC_URL_ENV;
249 FM_FORCE_BITCOIN_RPC_KIND: String = "bitcoind"; env: FM_FORCE_BITCOIN_RPC_KIND_ENV;
250 }
251}