tempo_commonware_node/args.rs
1//! Command line arguments for configuring the consensus layer of a tempo node.
2use std::{net::SocketAddr, path::PathBuf, sync::OnceLock};
3
4use commonware_cryptography::ed25519::PublicKey;
5use eyre::Context;
6use tempo_commonware_node_config::SigningKey;
7
8const DEFAULT_MAX_MESSAGE_SIZE_BYTES: usize = reth_consensus_common::validation::MAX_RLP_BLOCK_SIZE;
9
10/// Command line arguments for configuring the consensus layer of a tempo node.
11#[derive(Debug, Clone, PartialEq, Eq, clap::Args)]
12pub struct Args {
13 /// The file containing the ed25519 signing key for p2p communication.
14 #[arg(
15 long = "consensus.signing-key",
16 required_unless_present_any = ["follow", "dev"],
17 )]
18 signing_key: Option<PathBuf>,
19
20 /// The file containing a share of the bls12-381 threshold signing key.
21 #[arg(long = "consensus.signing-share")]
22 pub signing_share: Option<PathBuf>,
23
24 /// The socket address that will be bound to listen for consensus communication from
25 /// other nodes.
26 #[arg(long = "consensus.listen-address", default_value = "127.0.0.1:8000")]
27 pub listen_address: SocketAddr,
28
29 /// The socket address that will be bound to export consensus specific
30 /// metrics.
31 #[arg(long = "consensus.metrics-address", default_value = "127.0.0.1:8001")]
32 pub metrics_address: SocketAddr,
33
34 #[arg(long = "consensus.max-message-size-bytes", default_value_t = DEFAULT_MAX_MESSAGE_SIZE_BYTES)]
35 pub max_message_size_bytes: usize,
36
37 // pub storage_directory: camino::Utf8PathBuf,
38 /// The number of worker threads assigned to consensus.
39 #[arg(long = "consensus.worker-threads", default_value_t = 3)]
40 pub worker_threads: usize,
41
42 /// The maximum number of messages that can be cute on the various consensus
43 /// p2p channels before blocking.
44 #[arg(long = "consensus.message-backlog", default_value_t = 16_384)]
45 pub message_backlog: usize,
46
47 /// The overall number of items that can be received on the various consensus
48 /// p2p channels before blocking.
49 #[arg(long = "consensus.mailbox-size", default_value_t = 16_384)]
50 pub mailbox_size: usize,
51
52 /// The maximum number of blocks that will be buffered per peer. Used to
53 /// send and receive blocks over the p2p network of the consensus layer.
54 #[arg(long = "consensus.deque-size", default_value_t = 10)]
55 pub deque_size: usize,
56
57 /// The fee recipien that will be specified by this node. Will use the
58 /// coinbase address in genesis if not set.
59 #[arg(
60 long = "consensus.fee-recipient",
61 required_unless_present_any = ["follow", "dev"],
62 )]
63 pub fee_recipient: Option<alloy_primitives::Address>,
64
65 // The amount of time to wait for a peer to respond to a consensus request.
66 #[arg(long = "consensus.wait-for-peer-response", default_value = "2s")]
67 pub wait_for_peer_response: jiff::SignedDuration,
68
69 /// The amount of time to wait for a quorum of notarizations in a view
70 /// before attempting to skip the view.
71 #[arg(long = "consensus.wait-for-notarizations", default_value = "2s")]
72 pub wait_for_notarizations: jiff::SignedDuration,
73
74 /// Amount of time to wait to receive a proposal from the leader of the
75 /// current view.
76 #[arg(long = "consensus.wait-for-proposal", default_value = "2s")]
77 pub wait_for_proposal: jiff::SignedDuration,
78
79 /// The amount of time to wait before retrying a nullify broadcast if stuck
80 /// in a view.
81 #[arg(long = "consensus.wait-to-rebroadcast-nullify", default_value = "10s")]
82 pub wait_to_rebroadcast_nullify: jiff::SignedDuration,
83
84 /// The number of views (like voting rounds) to track. Also called an
85 /// activity timeout.
86 #[arg(long = "consensus.views-to-track", default_value_t = 256)]
87 pub views_to_track: u64,
88
89 /// The number of views (voting rounds) a validator is allowed to be
90 /// inactive until it is immediately skipped should leader selection pick it
91 /// as a proposer. Also called a skip timeout.
92 #[arg(
93 long = "consensus.inactive-views-until-leader-skip",
94 default_value_t = 32
95 )]
96 pub inactive_views_until_leader_skip: u64,
97
98 /// The amount of time this node will use to construct a block as a proposal.
99 /// This value should be well below `consensus.wait-for-proposal` to account
100 /// for the leader to enter the view, build and broadcast the proposal, and
101 /// have the other peers receive the proposal.
102 #[arg(long = "consensus.time-to-build-proposal", default_value = "500ms")]
103 pub time_to_build_proposal: jiff::SignedDuration,
104
105 /// The amount of time this node will use to construct a subblock before
106 /// sending it to the next proposer. This value should be well below
107 /// `consensus.time-to-build-proposal` to ensure the subblock is received
108 /// before the build is complete.
109 #[arg(long = "consensus.time-to-build-subblock", default_value = "100ms")]
110 pub time_to_build_subblock: jiff::SignedDuration,
111
112 /// Reduces security by disabling IP-based connection filtering.
113 /// Connections are still authenticated via public key cryptography, but
114 /// anyone can attempt handshakes, increasing exposure to DoS attacks.
115 /// Only enable in trusted network environments.
116 #[arg(
117 long = "consensus.allow-unregistered-handshakes",
118 default_value_t = false
119 )]
120 pub allow_unregistered_handshakes: bool,
121
122 /// The interval at which to broadcast subblocks to the next proposer.
123 /// Each built subblock is immediately broadcasted to the next proposer (if it's known).
124 /// We broadcast subblock every `subblock-broadcast-interval` to ensure the next
125 /// proposer is aware of the subblock even if they were slightly behind the chain
126 /// once we sent it in the first time.
127 #[arg(long = "consensus.subblock-broadcast-interval", default_value = "50ms")]
128 pub subblock_broadcast_interval: jiff::SignedDuration,
129
130 /// Cache for the signing key loaded from CLI-provided file.
131 #[clap(skip)]
132 loaded_signing_key: OnceLock<Option<SigningKey>>,
133}
134
135impl Args {
136 /// Returns the signing key loaded from specified file.
137 pub(crate) fn signing_key(&self) -> eyre::Result<Option<SigningKey>> {
138 if let Some(signing_key) = self.loaded_signing_key.get() {
139 return Ok(signing_key.clone());
140 }
141
142 let signing_key = self
143 .signing_key
144 .as_ref()
145 .map(|path| {
146 SigningKey::read_from_file(path).wrap_err_with(|| {
147 format!(
148 "failed reading private ed25519 signing key share from file `{}`",
149 path.display()
150 )
151 })
152 })
153 .transpose()?;
154
155 let _ = self.loaded_signing_key.set(signing_key.clone());
156
157 Ok(signing_key)
158 }
159
160 /// Returns the public key derived from the configured signing key, if any.
161 pub fn public_key(&self) -> eyre::Result<Option<PublicKey>> {
162 Ok(self
163 .signing_key()?
164 .map(|signing_key| signing_key.public_key()))
165 }
166}