Skip to main content

hotshot_libp2p_networking/network/
def.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7use hotshot_types::traits::signature_key::SignatureKey;
8use libp2p::{
9    Multiaddr, autonat,
10    gossipsub::{Behaviour as GossipBehaviour, Event as GossipEvent, IdentTopic},
11    identify::{Behaviour as IdentifyBehaviour, Event as IdentifyEvent},
12    kad::store::MemoryStore,
13    request_response::{OutboundRequestId, ResponseChannel},
14};
15use libp2p_identity::PeerId;
16use libp2p_swarm_derive::NetworkBehaviour;
17use tracing::{error, info};
18
19use super::{
20    NetworkEventInternal,
21    behaviours::dht::store::{
22        persistent::{DhtPersistentStorage, PersistentStore},
23        validated::ValidatedStore,
24    },
25    cbor,
26    log_summary::LogEvent,
27};
28
29/// Overarching network behaviour performing:
30/// - network topology discovery
31/// - direct messaging
32/// - p2p broadcast
33/// - connection management
34#[derive(NetworkBehaviour, derive_more::Debug)]
35#[behaviour(to_swarm = "NetworkEventInternal")]
36pub struct NetworkDef<K: SignatureKey + 'static, D: DhtPersistentStorage> {
37    /// purpose: broadcasting messages to many peers
38    /// NOTE gossipsub works ONLY for sharing messages right now
39    /// in the future it may be able to do peer discovery and routing
40    /// <https://github.com/libp2p/rust-libp2p/issues/2398>
41    #[debug(skip)]
42    gossipsub: GossipBehaviour,
43
44    /// The DHT store. We use a `PersistentStore` to occasionally save the DHT to
45    /// some persistent store and a `ValidatedStore` to validate the records stored.
46    #[debug(skip)]
47    pub dht: libp2p::kad::Behaviour<PersistentStore<ValidatedStore<MemoryStore, K>, D>>,
48
49    /// purpose: identifying the addresses from an outside POV
50    #[debug(skip)]
51    identify: IdentifyBehaviour,
52
53    /// purpose: directly messaging peer
54    #[debug(skip)]
55    pub direct_message: cbor::Behaviour<Vec<u8>, Vec<u8>>,
56
57    /// Auto NAT behaviour to determine if we are publicly reachable and
58    /// by which address
59    #[debug(skip)]
60    pub autonat: libp2p::autonat::Behaviour,
61}
62
63impl<K: SignatureKey + 'static, D: DhtPersistentStorage> NetworkDef<K, D> {
64    /// Create a new instance of a `NetworkDef`
65    #[must_use]
66    pub fn new(
67        gossipsub: GossipBehaviour,
68        dht: libp2p::kad::Behaviour<PersistentStore<ValidatedStore<MemoryStore, K>, D>>,
69        identify: IdentifyBehaviour,
70        direct_message: super::cbor::Behaviour<Vec<u8>, Vec<u8>>,
71        autonat: autonat::Behaviour,
72    ) -> NetworkDef<K, D> {
73        Self {
74            gossipsub,
75            dht,
76            identify,
77            direct_message,
78            autonat,
79        }
80    }
81}
82
83/// Address functions
84impl<K: SignatureKey + 'static, D: DhtPersistentStorage> NetworkDef<K, D> {
85    /// Add an address
86    pub fn add_address(&mut self, peer_id: &PeerId, address: Multiaddr) {
87        // NOTE to get this address to play nice with the other
88        // behaviours using the DHT for routing
89        // we only need to add this address to the DHT since it
90        // is always enabled. If it were not always enabled,
91        // we would need to manually add the address to
92        // the direct message behaviour
93        self.dht.add_address(peer_id, address);
94    }
95}
96
97/// Gossip functions
98impl<K: SignatureKey + 'static, D: DhtPersistentStorage> NetworkDef<K, D> {
99    /// Publish a given gossip
100    pub fn publish_gossip(&mut self, topic: IdentTopic, contents: Vec<u8>) {
101        if let Err(e) = self.gossipsub.publish(topic, contents) {
102            LogEvent::GossipPublishFailure.record();
103            tracing::debug!("Failed to publish gossip message. Error: {:?}", e);
104        }
105    }
106    /// Subscribe to a given topic
107    pub fn subscribe_gossip(&mut self, t: &str) {
108        if let Err(e) = self.gossipsub.subscribe(&IdentTopic::new(t)) {
109            error!("Failed to subscribe to topic {:?}. Error: {:?}", t, e);
110        }
111    }
112
113    /// Unsubscribe from a given topic
114    pub fn unsubscribe_gossip(&mut self, t: &str) {
115        if !self.gossipsub.unsubscribe(&IdentTopic::new(t)) {
116            info!("We were not subscribed to topic {:?}.", t);
117        }
118    }
119}
120
121/// Request/response functions
122impl<K: SignatureKey + 'static, D: DhtPersistentStorage> NetworkDef<K, D> {
123    /// Add a direct request for a given peer
124    pub fn add_direct_request(&mut self, peer_id: PeerId, data: Vec<u8>) -> OutboundRequestId {
125        self.direct_message.send_request(&peer_id, data)
126    }
127
128    /// Add a direct response for a channel
129    pub fn add_direct_response(&mut self, chan: ResponseChannel<Vec<u8>>, msg: Vec<u8>) {
130        let _ = self.direct_message.send_response(chan, msg);
131    }
132}
133
134impl From<GossipEvent> for NetworkEventInternal {
135    fn from(event: GossipEvent) -> Self {
136        Self::GossipEvent(Box::new(event))
137    }
138}
139
140impl From<libp2p::kad::Event> for NetworkEventInternal {
141    fn from(event: libp2p::kad::Event) -> Self {
142        Self::DHTEvent(event)
143    }
144}
145
146impl From<IdentifyEvent> for NetworkEventInternal {
147    fn from(event: IdentifyEvent) -> Self {
148        Self::IdentifyEvent(Box::new(event))
149    }
150}
151impl From<libp2p::request_response::Event<Vec<u8>, Vec<u8>>> for NetworkEventInternal {
152    fn from(value: libp2p::request_response::Event<Vec<u8>, Vec<u8>>) -> Self {
153        Self::DMEvent(value)
154    }
155}
156
157impl From<libp2p::autonat::Event> for NetworkEventInternal {
158    fn from(event: libp2p::autonat::Event) -> Self {
159        Self::AutonatEvent(event)
160    }
161}