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}