1use std::{
3 net::SocketAddr, num::NonZeroU32, path::PathBuf, str::FromStr, sync::OnceLock, time::Duration,
4};
5
6use commonware_cryptography::ed25519::PublicKey;
7use eyre::Context;
8use tempo_commonware_node_config::SigningKey;
9
10const DEFAULT_MAX_MESSAGE_SIZE_BYTES: u32 =
11 reth_consensus_common::validation::MAX_RLP_BLOCK_SIZE as u32;
12
13#[derive(Debug, Clone, clap::Args)]
15pub struct Args {
16 #[arg(
18 long = "consensus.signing-key",
19 required_unless_present_any = ["follow", "dev"],
20 )]
21 signing_key: Option<PathBuf>,
22
23 #[arg(long = "consensus.signing-share")]
25 pub signing_share: Option<PathBuf>,
26
27 #[arg(long = "consensus.listen-address", default_value = "127.0.0.1:8000")]
30 pub listen_address: SocketAddr,
31
32 #[arg(long = "consensus.metrics-address", default_value = "127.0.0.1:8001")]
35 pub metrics_address: SocketAddr,
36
37 #[arg(long = "consensus.max-message-size-bytes", default_value_t = DEFAULT_MAX_MESSAGE_SIZE_BYTES)]
38 pub max_message_size_bytes: u32,
39
40 #[arg(long = "consensus.worker-threads", default_value_t = 3)]
42 pub worker_threads: usize,
43
44 #[arg(long = "consensus.message-backlog", default_value_t = 16_384)]
47 pub message_backlog: usize,
48
49 #[arg(long = "consensus.mailbox-size", default_value_t = 16_384)]
52 pub mailbox_size: usize,
53
54 #[arg(long = "consensus.deque-size", default_value_t = 10)]
57 pub deque_size: usize,
58
59 #[arg(
62 long = "consensus.fee-recipient",
63 required_unless_present_any = ["follow", "dev"],
64 )]
65 pub fee_recipient: Option<alloy_primitives::Address>,
66
67 #[arg(long = "consensus.wait-for-peer-response", default_value = "2s")]
69 pub wait_for_peer_response: PositiveDuration,
70
71 #[arg(long = "consensus.wait-for-notarizations", default_value = "2s")]
74 pub wait_for_notarizations: PositiveDuration,
75
76 #[arg(long = "consensus.wait-for-proposal", default_value = "1200ms")]
79 pub wait_for_proposal: PositiveDuration,
80
81 #[arg(long = "consensus.wait-to-rebroadcast-nullify", default_value = "10s")]
84 pub wait_to_rebroadcast_nullify: PositiveDuration,
85
86 #[arg(long = "consensus.views-to-track", default_value_t = 256)]
89 pub views_to_track: u64,
90
91 #[arg(
95 long = "consensus.inactive-views-until-leader-skip",
96 default_value_t = 32
97 )]
98 pub inactive_views_until_leader_skip: u64,
99
100 #[arg(
105 long = "consensus.time-to-prepare-proposal-transactions",
106 default_value = "200ms"
107 )]
108 pub time_to_prepare_proposal_transactions: PositiveDuration,
109
110 #[arg(
117 long = "consensus.minimum-time-before-propose",
118 alias = "consensus.time-to-build-proposal",
119 default_value = "450ms"
120 )]
121 pub minimum_time_before_propose: PositiveDuration,
122
123 #[arg(long = "consensus.enable-subblocks", default_value_t = false)]
128 pub enable_subblocks: bool,
129
130 #[arg(long = "consensus.time-to-build-subblock", default_value = "100ms")]
135 pub time_to_build_subblock: PositiveDuration,
136
137 #[arg(long = "consensus.use-local-defaults", default_value_t = false)]
140 pub use_local_defaults: bool,
141
142 #[arg(long = "consensus.bypass-ip-check", default_value_t = false)]
147 pub bypass_ip_check: bool,
148
149 #[arg(
151 long = "consensus.allow-private-ips",
152 default_value_t = false,
153 default_value_if("use_local_defaults", "true", "true")
154 )]
155 pub allow_private_ips: bool,
156
157 #[arg(long = "consensus.allow-dns", default_value_t = true)]
159 pub allow_dns: bool,
160
161 #[arg(long = "consensus.synchrony-bound", default_value = "5s")]
163 pub synchrony_bound: PositiveDuration,
164
165 #[arg(
168 long = "consensus.wait-before-peers-redial",
169 default_value = "1s",
170 default_value_if("use_local_defaults", "true", "500ms")
171 )]
172 pub wait_before_peers_redial: PositiveDuration,
173
174 #[arg(
176 long = "consensus.wait-before-peers-reping",
177 default_value = "50s",
178 default_value_if("use_local_defaults", "true", "5s")
179 )]
180 pub wait_before_peers_reping: PositiveDuration,
181
182 #[arg(
184 long = "consensus.wait-before-peers-discovery",
185 default_value = "60s",
186 default_value_if("use_local_defaults", "true", "30s")
187 )]
188 pub wait_before_peers_discovery: PositiveDuration,
189
190 #[arg(
193 long = "consensus.connection-per-peer-min-period",
194 default_value = "60s",
195 default_value_if("use_local_defaults", "true", "1s")
196 )]
197 pub connection_per_peer_min_period: PositiveDuration,
198
199 #[arg(
202 long = "consensus.handshake-per-ip-min-period",
203 default_value = "5s",
204 default_value_if("use_local_defaults", "true", "62ms")
205 )]
206 pub handshake_per_ip_min_period: PositiveDuration,
207
208 #[arg(
211 long = "consensus.handshake-per-subnet-min-period",
212 default_value = "15ms",
213 default_value_if("use_local_defaults", "true", "7ms")
214 )]
215 pub handshake_per_subnet_min_period: PositiveDuration,
216
217 #[arg(long = "consensus.handshake-stale-after", default_value = "10s")]
219 pub handshake_stale_after: PositiveDuration,
220
221 #[arg(long = "consensus.handshake-timeout", default_value = "5s")]
223 pub handshake_timeout: PositiveDuration,
224
225 #[arg(
227 long = "consensus.max-concurrent-handshakes",
228 default_value = "512",
229 default_value_if("use_local_defaults", "true", "1024")
230 )]
231 pub max_concurrent_handshakes: NonZeroU32,
232
233 #[arg(
235 long = "consensus.time-to-unblock-byzantine-peer",
236 default_value = "4h",
237 default_value_if("use_local_defaults", "true", "1h")
238 )]
239 pub time_to_unblock_byzantine_peer: PositiveDuration,
240
241 #[arg(long = "consensus.backfill-frequency", default_value = "8")]
243 pub backfill_frequency: std::num::NonZeroU32,
244
245 #[arg(long = "consensus.subblock-broadcast-interval", default_value = "50ms")]
251 pub subblock_broadcast_interval: PositiveDuration,
252
253 #[arg(long = "consensus.fcu-heartbeat-interval", default_value = "5m")]
258 pub fcu_heartbeat_interval: PositiveDuration,
259
260 #[clap(skip)]
262 loaded_signing_key: OnceLock<Option<SigningKey>>,
263
264 #[arg(long = "consensus.datadir", value_name = "PATH")]
267 pub storage_dir: Option<PathBuf>,
268}
269
270#[derive(Debug, Clone, Copy)]
272pub struct PositiveDuration(jiff::SignedDuration);
273impl PositiveDuration {
274 pub fn into_duration(self) -> Duration {
275 self.0
276 .try_into()
277 .expect("must be positive. enforced when cli parsing.")
278 }
279}
280
281impl FromStr for PositiveDuration {
282 type Err = Box<dyn std::error::Error + Send + Sync + 'static>;
283
284 fn from_str(s: &str) -> Result<Self, Self::Err> {
285 let duration = s.parse::<jiff::SignedDuration>()?;
286 let _: Duration = duration.try_into().wrap_err("duration must be positive")?;
287
288 Ok(Self(duration))
289 }
290}
291
292impl Args {
293 pub(crate) fn signing_key(&self) -> eyre::Result<Option<SigningKey>> {
295 if let Some(signing_key) = self.loaded_signing_key.get() {
296 return Ok(signing_key.clone());
297 }
298
299 let signing_key = self
300 .signing_key
301 .as_ref()
302 .map(|path| {
303 SigningKey::read_from_file(path).wrap_err_with(|| {
304 format!(
305 "failed reading private ed25519 signing key share from file `{}`",
306 path.display()
307 )
308 })
309 })
310 .transpose()?;
311
312 let _ = self.loaded_signing_key.set(signing_key.clone());
313
314 Ok(signing_key)
315 }
316
317 pub fn public_key(&self) -> eyre::Result<Option<PublicKey>> {
319 Ok(self
320 .signing_key()?
321 .map(|signing_key| signing_key.public_key()))
322 }
323}