tempo_commonware_node/
lib.rs

1//! A Tempo node using commonware's threshold simplex as consensus.
2
3#![cfg_attr(not(test), warn(unused_crate_dependencies))]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6pub(crate) mod alias;
7mod args;
8pub(crate) mod config;
9pub mod consensus;
10pub(crate) mod dkg;
11pub(crate) mod epoch;
12pub mod metrics;
13
14pub(crate) mod subblocks;
15
16use std::net::SocketAddr;
17
18use commonware_cryptography::ed25519::{PrivateKey, PublicKey};
19use commonware_p2p::authenticated::lookup;
20use commonware_runtime::Metrics as _;
21use eyre::{OptionExt, WrapErr as _, eyre};
22use tempo_commonware_node_config::SigningShare;
23use tempo_node::TempoFullNode;
24
25use crate::config::{
26    BOUNDARY_CERT_CHANNEL_IDENT, BOUNDARY_CERT_LIMIT, BROADCASTER_CHANNEL_IDENT, BROADCASTER_LIMIT,
27    DKG_CHANNEL_IDENT, DKG_LIMIT, MARSHAL_CHANNEL_IDENT, MARSHAL_LIMIT, PEERSETS_TO_TRACK,
28    PENDING_CHANNEL_IDENT, PENDING_LIMIT, RECOVERED_CHANNEL_IDENT, RECOVERED_LIMIT,
29    RESOLVER_CHANNEL_IDENT, RESOLVER_LIMIT, SUBBLOCKS_CHANNEL_IDENT, SUBBLOCKS_LIMIT,
30};
31
32pub use args::Args;
33
34pub async fn run_consensus_stack(
35    context: &commonware_runtime::tokio::Context,
36    config: Args,
37    execution_node: TempoFullNode,
38) -> eyre::Result<()> {
39    let share = config
40        .signing_share
41        .as_ref()
42        .map(|share| {
43            SigningShare::read_from_file(share).wrap_err_with(|| {
44                format!(
45                    "failed reading private bls12-381 key share from file `{}`",
46                    share.display()
47                )
48            })
49        })
50        .transpose()?
51        .map(|signing_share| signing_share.into_inner());
52
53    let signing_key = config
54        .signing_key()?
55        .ok_or_eyre("required option `consensus.signing-key` not set")?;
56
57    let (mut network, oracle) = instantiate_network(
58        context,
59        signing_key.clone().into_inner(),
60        config.listen_address,
61        config.mailbox_size,
62        config.max_message_size_bytes,
63        config.allow_unregistered_handshakes,
64    )
65    .await
66    .wrap_err("failed to start network")?;
67
68    let message_backlog = config.message_backlog;
69    let pending = network.register(PENDING_CHANNEL_IDENT, PENDING_LIMIT, message_backlog);
70    let recovered = network.register(RECOVERED_CHANNEL_IDENT, RECOVERED_LIMIT, message_backlog);
71    let resolver = network.register(RESOLVER_CHANNEL_IDENT, RESOLVER_LIMIT, message_backlog);
72    let broadcaster = network.register(
73        BROADCASTER_CHANNEL_IDENT,
74        BROADCASTER_LIMIT,
75        message_backlog,
76    );
77    let marshal = network.register(MARSHAL_CHANNEL_IDENT, MARSHAL_LIMIT, message_backlog);
78    let dkg = network.register(DKG_CHANNEL_IDENT, DKG_LIMIT, message_backlog);
79    let boundary_certificates = network.register(
80        BOUNDARY_CERT_CHANNEL_IDENT,
81        BOUNDARY_CERT_LIMIT,
82        message_backlog,
83    );
84    let subblocks = network.register(SUBBLOCKS_CHANNEL_IDENT, SUBBLOCKS_LIMIT, message_backlog);
85
86    let fee_recipient = config
87        .fee_recipient
88        .ok_or_eyre("required option `consensus.fee-recipient` not set")?;
89
90    let consensus_engine = crate::consensus::engine::Builder {
91        context: context.with_label("engine"),
92
93        fee_recipient,
94
95        execution_node: Some(execution_node),
96        blocker: oracle.clone(),
97        peer_manager: oracle.clone(),
98        // TODO: Set this through config?
99        partition_prefix: "engine".into(),
100        signer: signing_key.into_inner(),
101        share,
102        mailbox_size: config.mailbox_size,
103        deque_size: config.deque_size,
104
105        time_to_propose: config.wait_for_proposal.try_into().wrap_err(
106            "failed converting argument wait-for-proposal to regular duration; \
107            was it negative or chosen too large?",
108        )?,
109        time_to_collect_notarizations: config.wait_for_notarizations.try_into().wrap_err(
110            "failed converting argument wait-for-notarizations to regular \
111            duration; was it negative or chosen too large",
112        )?,
113        time_to_retry_nullify_broadcast: config.wait_to_rebroadcast_nullify.try_into().wrap_err(
114            "failed converting argument wait-to-rebroadcast-nullify to regular \
115            duration; was it negative or chosen too large",
116        )?,
117        time_for_peer_response: config.wait_for_peer_response.try_into().wrap_err(
118            "failed converting argument wait-for-peer-response to regular \
119            duration; was it negative or chosen too large",
120        )?,
121        views_to_track: config.views_to_track,
122        views_until_leader_skip: config.inactive_views_until_leader_skip,
123        new_payload_wait_time: config.time_to_build_proposal.try_into().wrap_err(
124            "failed converting argument time-to-build-proposal to regular \
125            duration; was it negative or chosen too large",
126        )?,
127        time_to_build_subblock: config.time_to_build_subblock.try_into().wrap_err(
128            "failed converting argument time-to-build-subblock to regular \
129            duration; was it negative or chosen too large",
130        )?,
131        subblock_broadcast_interval: config.subblock_broadcast_interval.try_into().wrap_err(
132            "failed converting argument subblock-broadcast-interval to regular \
133            duration; was it negative or chosen too large",
134        )?,
135    }
136    .try_init()
137    .await
138    .wrap_err("failed initializing consensus engine")?;
139
140    let (network, consensus_engine) = (
141        network.start(),
142        consensus_engine.start(
143            pending,
144            recovered,
145            resolver,
146            broadcaster,
147            marshal,
148            dkg,
149            boundary_certificates,
150            subblocks,
151        ),
152    );
153
154    tokio::select! {
155        ret = network => {
156            ret.map_err(eyre::Report::from)
157                .and_then(|()| Err(eyre!("exited unexpectedly")))
158                .wrap_err("network task failed")
159        }
160
161        ret = consensus_engine => {
162            ret.map_err(eyre::Report::from)
163                .and_then(|ret| ret.and_then(|()| Err(eyre!("exited unexpectedly"))))
164                .wrap_err("consensus engine task failed")
165        }
166    }
167}
168
169async fn instantiate_network(
170    context: &commonware_runtime::tokio::Context,
171    signing_key: PrivateKey,
172    listen_addr: SocketAddr,
173    mailbox_size: usize,
174    max_message_size: usize,
175    allow_unregistered_handshakes: bool,
176) -> eyre::Result<(
177    lookup::Network<commonware_runtime::tokio::Context, PrivateKey>,
178    lookup::Oracle<PublicKey>,
179)> {
180    // TODO: Find out why `union_unique` should be used at all. This is the only place
181    // where `NAMESPACE` is used at all. We follow alto's example for now.
182    let p2p_namespace = commonware_utils::union_unique(crate::config::NAMESPACE, b"_P2P");
183    let p2p_cfg = lookup::Config {
184        mailbox_size,
185        tracked_peer_sets: PEERSETS_TO_TRACK,
186        attempt_unregistered_handshakes: allow_unregistered_handshakes,
187        ..lookup::Config::local(signing_key, &p2p_namespace, listen_addr, max_message_size)
188    };
189
190    Ok(lookup::Network::new(context.with_label("network"), p2p_cfg))
191}