Skip to main content

hotshot_new_protocol/
network.rs

1use std::{
2    collections::{HashMap, HashSet},
3    sync::Arc,
4};
5
6pub use cliquenet::{Config as CliquenetConfig, NetAddr, Role};
7use cliquenet::{NetworkReceiver, NetworkSender, Slot, noise::Protocol, x25519::PublicKey};
8use hotshot_types::{
9    PeerConnectInfo,
10    data::{EpochNumber, ViewNumber},
11    epoch_membership::EpochMembershipCoordinator,
12    message::{EXTERNAL_MESSAGE_VERSION, MessageKind, UpgradeLock},
13    traits::{
14        metrics::{Counter, CounterFamily, Gauge, GaugeFamily, Metrics},
15        node_implementation::NodeType,
16    },
17    x25519::{self, Keypair},
18};
19use hotshot_utils::anytrace;
20use parking_lot::RwLock;
21use tracing::{error, info};
22
23use crate::message::{Message, MessageType, Unchecked, Validated};
24
25#[derive(Debug)]
26pub struct Cliquenet<T: NodeType> {
27    inner: Sender<T>,
28    receiver: NetworkReceiver,
29}
30
31#[derive(Debug, Clone)]
32pub struct Sender<T: NodeType> {
33    my_keys: (T::SignatureKey, PublicKey),
34    sender: NetworkSender,
35    shared: Arc<RwLock<Shared<T::SignatureKey>>>,
36    upgrade_lock: UpgradeLock<T>,
37}
38
39#[derive(Debug)]
40struct Shared<K> {
41    peers: HashMap<K, PeerConnectInfo>,
42    epoch: EpochNumber,
43}
44
45impl<T: NodeType> Cliquenet<T> {
46    pub async fn create<A, P, S>(
47        name: S,
48        signing_key: T::SignatureKey,
49        keypair: Keypair,
50        addr: A,
51        parties: P,
52        upgrade_lock: UpgradeLock<T>,
53        metrics: Box<dyn Metrics>,
54    ) -> Result<Self, NetworkError>
55    where
56        A: Into<cliquenet::NetAddr>,
57        P: IntoIterator<Item = (T::SignatureKey, PeerConnectInfo)>,
58        S: Into<String>,
59    {
60        let parties: HashMap<T::SignatureKey, PeerConnectInfo> = parties.into_iter().collect();
61
62        let cfg = cliquenet::Config::builder()
63            .name(name)
64            .keypair(keypair.into())
65            .bind(addr.into())
66            .parties(
67                parties
68                    .values()
69                    .map(|info| (info.x25519_key.into(), info.p2p_addr.clone())),
70            )
71            .noise_protocols([(1.into(), Protocol::IK_25519_AesGcm_Blake2s)])
72            .build();
73
74        Self::create_with_config(signing_key, upgrade_lock, cfg, parties, metrics).await
75    }
76
77    pub(crate) async fn create_with_config<P>(
78        signing_key: T::SignatureKey,
79        upgrade_lock: UpgradeLock<T>,
80        config: cliquenet::Config,
81        parties: P,
82        metrics: Box<dyn Metrics>,
83    ) -> Result<Self, NetworkError>
84    where
85        P: IntoIterator<Item = (T::SignatureKey, PeerConnectInfo)>,
86    {
87        let public_key = config.public_key();
88        let metrics = CliquenetMetrics::new(metrics);
89        let network = cliquenet::Network::create(config.with_metrics(metrics)).await?;
90        let peers: HashMap<_, _> = parties.into_iter().collect();
91
92        info!(peers = %peers.len(), "cliquenet created");
93
94        let (send, recv) = network.split_into();
95
96        Ok(Self {
97            inner: Sender {
98                my_keys: (signing_key, public_key),
99                sender: send,
100                shared: Arc::new(RwLock::new(Shared {
101                    peers,
102                    epoch: EpochNumber::new(0),
103                })),
104                upgrade_lock,
105            },
106            receiver: recv,
107        })
108    }
109
110    pub fn sender(&self) -> &Sender<T> {
111        &self.inner
112    }
113
114    pub async fn receive(&mut self) -> Result<Message<T, Unchecked>, NetworkError> {
115        let (src, bytes) = self
116            .receiver
117            .receive()
118            .await
119            .ok_or(cliquenet::NetworkError::ChannelClosed)?;
120        let msg = self.deserialize(&bytes)?;
121        let key = self
122            .inner
123            .shared
124            .read()
125            .peers
126            .get(&msg.sender)
127            .map(|info| info.x25519_key)
128            .or_else(|| {
129                (msg.sender == self.inner.my_keys.0).then_some(self.inner.my_keys.1.into())
130            });
131        if Some(src.into()) != key {
132            return Err(NetworkError::InvalidSender {
133                msg: key,
134                src: src.into(),
135            });
136        }
137        Ok(msg)
138    }
139
140    pub async fn shutdown(&mut self) {
141        if let Ok(done) = self.inner.sender.shutdown() {
142            done.await
143        }
144    }
145
146    pub fn gc(&mut self, v: ViewNumber) -> Result<(), NetworkError> {
147        self.inner.sender.gc(Slot::new(*v))?;
148        Ok(())
149    }
150
151    pub fn add_peers(
152        &mut self,
153        r: Role,
154        ps: Vec<(T::SignatureKey, (PublicKey, NetAddr))>,
155    ) -> Result<(), NetworkError> {
156        let mut targets = Vec::new();
157        {
158            let mut shared = self.inner.shared.write();
159            for (k, (x, a)) in ps {
160                shared.peers.insert(
161                    k,
162                    PeerConnectInfo {
163                        x25519_key: x.into(),
164                        p2p_addr: a.clone(),
165                    },
166                );
167                targets.push((x, a))
168            }
169        }
170        self.inner.sender.add_peers(r, targets)?;
171        Ok(())
172    }
173
174    pub fn remove_peers(&mut self, ps: Vec<&T::SignatureKey>) -> Result<(), NetworkError> {
175        let mut targets = Vec::new();
176        {
177            let mut shared = self.inner.shared.write();
178            for k in ps {
179                if let Some(info) = shared.peers.remove(k) {
180                    targets.push(info.x25519_key.into())
181                }
182            }
183        }
184        self.inner.sender.remove_peers(targets)?;
185        Ok(())
186    }
187
188    pub fn assign_role(&mut self, r: Role, ps: Vec<&T::SignatureKey>) -> Result<(), NetworkError> {
189        let mut targets = Vec::new();
190        {
191            let shared = self.inner.shared.read();
192            for k in ps {
193                if let Some(info) = shared.peers.get(k) {
194                    targets.push(info.x25519_key.into())
195                }
196            }
197        }
198        self.inner.sender.assign_peers(r, targets)?;
199        Ok(())
200    }
201
202    /// Update peers on every epoch change.
203    ///
204    /// For any given epoch `e` we collect the validators of `e`, `e-1` and
205    /// `e+1` from the stake tables and merge their connection information.
206    ///
207    /// We keep validators that were in `e-1` but not in `e` for one additional
208    /// epoch and eagerly connect to new validators of `e+1`.
209    pub fn apply_epoch(
210        &mut self,
211        epoch: EpochNumber,
212        coord: &EpochMembershipCoordinator<T>,
213    ) -> Result<(), NetworkError> {
214        let ours = self.inner.shared.read().epoch;
215        if epoch <= ours {
216            info!(%epoch, %ours, "epoch already seen");
217            return Ok(());
218        }
219
220        // Validators of the new epoch.
221        let Some(curr_infos) = coord.epoch_peers(Some(epoch)) else {
222            error!(%epoch, "no stake table available");
223            return Ok(());
224        };
225
226        // Validators leaving are retained as peers for one additional epoch.
227        let prev_infos = if *epoch > 0 {
228            coord.epoch_peers(Some(epoch - 1)).unwrap_or_else(|| {
229                info!(%epoch, "previous epoch's stake table unavailable");
230                HashMap::new()
231            })
232        } else {
233            HashMap::new()
234        };
235
236        // Validators joining in the next epoch are connected to early.
237        let next_infos = coord.epoch_peers(Some(epoch + 1)).unwrap_or_else(|| {
238            info!(%epoch, "next epoch's stake table not available");
239            HashMap::new()
240        });
241
242        // Since connection information may be updated, we need to merge them,
243        // preferring the newest epoch's data, i.e. `next(curr(prev))`.
244        let mut merged_infos = prev_infos.clone();
245        for (k, v) in curr_infos.iter().chain(&next_infos) {
246            merged_infos.insert(k.clone(), v.clone());
247        }
248
249        let wanted: HashSet<T::SignatureKey> = curr_infos
250            .keys()
251            .chain(next_infos.keys())
252            .cloned()
253            .collect();
254
255        let retained: HashSet<T::SignatureKey> = curr_infos
256            .keys()
257            .chain(prev_infos.keys())
258            .cloned()
259            .collect();
260
261        let mut to_add: Vec<(T::SignatureKey, PeerConnectInfo)> = Vec::new();
262        let mut to_del: Vec<(T::SignatureKey, PeerConnectInfo)> = Vec::new();
263
264        for k in &wanted {
265            if let Some(Some(new_info)) = merged_infos.get(k) {
266                if Some(new_info) != self.inner.shared.read().peers.get(k) {
267                    info!(%epoch, peer = %k, "adding/updating network peer");
268                    to_add.push((k.clone(), new_info.clone()));
269                } else {
270                    info!(%epoch, peer = %k, "peer unchanged");
271                }
272            } else {
273                info!(%epoch, peer = %k, "ignoring peer without connection info");
274            }
275        }
276
277        // Remove peers that have left both the current and previous epochs.
278        for (k, info) in &self.inner.shared.read().peers {
279            if !(retained.contains(k) || wanted.contains(k)) {
280                info!(%epoch, peer = %k, "removing network peer");
281                to_del.push((k.clone(), info.clone()));
282            }
283        }
284
285        {
286            let peers = &mut self.inner.shared.write().peers;
287            for (k, _) in &to_del {
288                peers.remove(k);
289            }
290            for (k, info) in &to_add {
291                peers.insert(k.clone(), info.clone());
292            }
293        }
294
295        let add_targets: Vec<(PublicKey, NetAddr)> = to_add
296            .iter()
297            .map(|(_, i)| (i.x25519_key.into(), i.p2p_addr.clone()))
298            .collect();
299        let del_targets: Vec<PublicKey> = to_del.iter().map(|(_, i)| i.x25519_key.into()).collect();
300
301        if let Err(err) = self.inner.sender.add_peers(Role::Active, add_targets) {
302            error!(%epoch, %err, "network down; could not add peers to network");
303            return Err(err.into());
304        }
305
306        if let Err(err) = self.inner.sender.remove_peers(del_targets) {
307            error!(%epoch, %err, "network down; could not remove peers from network");
308            return Err(err.into());
309        }
310
311        info!(%epoch, peers = %self.inner.shared.read().peers.len());
312
313        self.inner.shared.write().epoch = epoch;
314
315        Ok(())
316    }
317
318    fn deserialize(&self, bytes: &[u8]) -> Result<Message<T, Unchecked>, NetworkError> {
319        match self
320            .inner
321            .upgrade_lock
322            .deserialize::<Message<T, Unchecked>>(bytes)
323        {
324            Ok((m, v)) => {
325                if v == EXTERNAL_MESSAGE_VERSION && !m.is_external() {
326                    let e = anytrace::warn!("received a non-external message with version 0.0");
327                    return Err(NetworkError::Serialize(e));
328                }
329                Ok(m)
330            },
331            Err(primary_err) => {
332                // Fallback: bytes may be a hotshot-types `Message<T>` carrying
333                // an `External` payload (this is how `Leaf2Fetcher` in the
334                // membership layer frames leaf-catchup requests/responses).
335                // If so, surface it as `MessageType::External` so the
336                // Coordinator can route it to the membership external
337                // channel just like a native new-protocol external message.
338                if let Ok((_v, hs_msg)) =
339                    versions::decode::<hotshot_types::message::Message<T>>(bytes)
340                    && let MessageKind::External(data) = hs_msg.kind
341                {
342                    return Ok(Message {
343                        sender: hs_msg.sender,
344                        message_type: MessageType::External(data),
345                    });
346                }
347                Err(NetworkError::Serialize(primary_err))
348            },
349        }
350    }
351}
352
353impl<T: NodeType> Sender<T> {
354    pub fn unicast(
355        &self,
356        v: ViewNumber,
357        to: &T::SignatureKey,
358        m: &Message<T, Validated>,
359    ) -> Result<(), NetworkError> {
360        let target = if *to == self.my_keys.0 {
361            self.my_keys.1
362        } else if let Some(info) = self.shared.read().peers.get(to) {
363            info.x25519_key.into()
364        } else {
365            error!(peer = %to, "unicast target not found");
366            return Ok(());
367        };
368        let bytes = self.serialize(m)?;
369        self.sender.unicast(Slot::new(*v), target, bytes)?;
370        Ok(())
371    }
372
373    pub fn multicast(
374        &self,
375        v: ViewNumber,
376        to: Vec<&T::SignatureKey>,
377        m: &Message<T, Validated>,
378    ) -> Result<(), NetworkError> {
379        let bytes = self.serialize(m)?;
380        let mut targets = Vec::new();
381        {
382            let shared = self.shared.read();
383            for t in to {
384                if let Some(info) = shared.peers.get(t) {
385                    targets.push(info.x25519_key.into())
386                } else if *t == self.my_keys.0 {
387                    targets.push(self.my_keys.1)
388                } else {
389                    error!(peer = %t, "multicast target not found");
390                }
391            }
392        }
393        self.sender.multicast(Slot::new(*v), targets, bytes)?;
394        Ok(())
395    }
396
397    pub fn broadcast(&self, v: ViewNumber, m: &Message<T, Validated>) -> Result<(), NetworkError> {
398        let bytes = self.serialize(m)?;
399        self.sender.broadcast(Slot::new(*v), bytes)?;
400        Ok(())
401    }
402
403    fn serialize(&self, m: &Message<T, Validated>) -> Result<Vec<u8>, NetworkError> {
404        if let MessageType::External(bytes) = &m.message_type {
405            return Ok(bytes.clone());
406        }
407        let v = self.upgrade_lock.serialize(m)?;
408        Ok(v)
409    }
410}
411
412#[derive(Debug, thiserror::Error)]
413pub enum NetworkError {
414    #[error("cliquenet: {0}")]
415    Cliquenet(#[from] cliquenet::NetworkError),
416
417    #[error("serialization: {0}")]
418    Serialize(#[from] anytrace::Error),
419
420    #[error("message sender {msg:?} != message source {src}")]
421    InvalidSender {
422        msg: Option<x25519::PublicKey>,
423        src: x25519::PublicKey,
424    },
425}
426
427impl NetworkError {
428    pub fn is_critical(&self) -> bool {
429        matches!(
430            self,
431            Self::Cliquenet(
432                cliquenet::NetworkError::Bind(..)
433                    | cliquenet::NetworkError::ChannelClosed
434                    | cliquenet::NetworkError::BudgetClosed
435            )
436        )
437    }
438}
439
440struct CliquenetMetrics {
441    metrics: Box<dyn Metrics>,
442    gauges: RwLock<Gauges>,
443    counters: RwLock<Counters>,
444}
445
446#[derive(Default)]
447struct Gauges {
448    gauges: HashMap<PublicKey, HashMap<String, Box<dyn Gauge>>>,
449    family: HashMap<String, Box<dyn GaugeFamily>>,
450}
451
452#[derive(Default)]
453struct Counters {
454    counters: HashMap<PublicKey, HashMap<String, Box<dyn Counter>>>,
455    family: HashMap<String, Box<dyn CounterFamily>>,
456}
457
458impl CliquenetMetrics {
459    pub fn new(m: Box<dyn Metrics>) -> Self {
460        Self {
461            metrics: m.subgroup("cliquenet".to_string()),
462            gauges: RwLock::new(Gauges::default()),
463            counters: RwLock::new(Counters::default()),
464        }
465    }
466}
467
468// In here we lazily create counters and gauges based on their labels.
469// If not found, we create a family using the label, e.g. "connect_attempts",
470// indexed by the peer (key). Afterwards we create the actual counter or gauge,
471// and update its value. On the next call, the metric would be found and
472// updated right away.
473impl cliquenet::Metrics for CliquenetMetrics {
474    fn set(&self, key: &PublicKey, label: &str, val: usize) {
475        if let Some(g) = self
476            .gauges
477            .read()
478            .gauges
479            .get(key)
480            .and_then(|m| m.get(label))
481        {
482            return g.set(val);
483        }
484
485        let mut gauges = self.gauges.write();
486
487        // Check again, in case a concurrent write has created the gauge:
488        if let Some(g) = gauges.gauges.get(key).and_then(|m| m.get(label)) {
489            return g.set(val);
490        }
491
492        let g = gauges
493            .family
494            .entry(label.to_string())
495            .or_insert_with(|| {
496                self.metrics
497                    .gauge_family(label.to_string(), vec!["peer".to_string()])
498            })
499            .create(vec![key.to_string()]);
500
501        gauges
502            .gauges
503            .entry(*key)
504            .or_default()
505            .entry(label.to_string())
506            .or_insert(g)
507            .set(val)
508    }
509
510    fn add(&self, key: &PublicKey, label: &str, val: usize) {
511        if let Some(c) = self
512            .counters
513            .read()
514            .counters
515            .get(key)
516            .and_then(|m| m.get(label))
517        {
518            return c.add(val);
519        }
520
521        let mut counters = self.counters.write();
522
523        // Check again, in case a concurrent write has created the counter:
524        if let Some(c) = counters.counters.get(key).and_then(|m| m.get(label)) {
525            return c.add(val);
526        }
527
528        let c = counters
529            .family
530            .entry(label.to_string())
531            .or_insert_with(|| {
532                self.metrics
533                    .counter_family(label.to_string(), vec!["peer".to_string()])
534            })
535            .create(vec![key.to_string()]);
536
537        counters
538            .counters
539            .entry(*key)
540            .or_default()
541            .entry(label.to_string())
542            .or_insert(c)
543            .add(val)
544    }
545
546    fn del(&self, key: &PublicKey) {
547        let key_string = key.to_string();
548
549        {
550            let mut gauges = self.gauges.write();
551            for (label, _) in gauges.gauges.remove(key).into_iter().flatten() {
552                if let Some(f) = gauges.family.get(&label) {
553                    f.destroy(&[&key_string]);
554                }
555            }
556        }
557
558        {
559            let mut counters = self.counters.write();
560            for (label, _) in counters.counters.remove(key).into_iter().flatten() {
561                if let Some(f) = counters.family.get(&label) {
562                    f.destroy(&[&key_string]);
563                }
564            }
565        }
566    }
567}