darkfi/net/protocol/
protocol_seed.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use async_trait::async_trait;
20use smol::{lock::RwLock as AsyncRwLock, Executor};
21use std::{sync::Arc, time::UNIX_EPOCH};
22use tracing::debug;
23
24use super::{
25    super::{
26        channel::ChannelPtr,
27        hosts::{HostColor, HostsPtr},
28        message::{AddrsMessage, GetAddrsMessage},
29        message_publisher::MessageSubscription,
30        p2p::P2pPtr,
31        settings::Settings,
32    },
33    protocol_base::{ProtocolBase, ProtocolBasePtr},
34};
35use crate::Result;
36
37/// Implements the seed protocol
38pub struct ProtocolSeed {
39    channel: ChannelPtr,
40    hosts: HostsPtr,
41    settings: Arc<AsyncRwLock<Settings>>,
42    addr_sub: MessageSubscription<AddrsMessage>,
43}
44
45const PROTO_NAME: &str = "ProtocolSeed";
46
47impl ProtocolSeed {
48    /// Create a new seed protocol.
49    pub async fn init(channel: ChannelPtr, p2p: P2pPtr) -> ProtocolBasePtr {
50        // Create a subscription to address message
51        let addr_sub =
52            channel.subscribe_msg::<AddrsMessage>().await.expect("Missing addr dispatcher!");
53
54        Arc::new(Self { channel, hosts: p2p.hosts(), settings: p2p.settings(), addr_sub })
55    }
56
57    /// Send our own external addresses over a channel. Set the
58    /// last_seen field to now.
59    pub async fn send_my_addrs(&self) -> Result<()> {
60        debug!(
61            target: "net::protocol_seed::send_my_addrs",
62            "[START] channel address={}", self.channel.display_address(),
63        );
64
65        let external_addrs = self.channel.hosts().external_addrs().await;
66
67        if external_addrs.is_empty() {
68            debug!(
69                target: "net::protocol_seed::send_my_addrs",
70                "External address is not configured. Stopping",
71            );
72            return Ok(())
73        }
74
75        let mut addrs = vec![];
76
77        for addr in external_addrs {
78            let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
79            addrs.push((addr, last_seen));
80        }
81
82        debug!(
83            target: "net::protocol_seed::send_my_addrs",
84            "Broadcasting {} addresses", addrs.len(),
85        );
86
87        let ext_addr_msg = AddrsMessage { addrs };
88        self.channel.send(&ext_addr_msg).await?;
89
90        debug!(
91            target: "net::protocol_seed::send_my_addrs",
92            "[END] channel address={}", self.channel.display_address(),
93        );
94
95        Ok(())
96    }
97}
98
99#[async_trait]
100impl ProtocolBase for ProtocolSeed {
101    /// Starts the seed protocol. Creates a subscription to the address
102    /// message.  If our external address is enabled, then send our address
103    /// to the seed server.  Sends a get-address message and receives an
104    /// address messsage.
105    async fn start(self: Arc<Self>, _ex: Arc<Executor<'_>>) -> Result<()> {
106        debug!(target: "net::protocol_seed::start", "START => address={}", self.channel.display_address());
107
108        // Send own address to the seed server
109        self.send_my_addrs().await?;
110
111        let settings = self.settings.read().await;
112        let outbound_connections = settings.outbound_connections;
113        let getaddrs_max = settings.getaddrs_max;
114        let active_profiles = settings.active_profiles.clone();
115        drop(settings);
116
117        // Send get address message
118        // We ask for a maximum of u8::MAX addresses from a single node
119        let get_addr = GetAddrsMessage {
120            max: getaddrs_max.unwrap_or(outbound_connections.min(u32::MAX as usize) as u32),
121            transports: active_profiles,
122        };
123        self.channel.send(&get_addr).await?;
124
125        // Receive addresses
126        let addrs_msg = self.addr_sub.receive().await?;
127        debug!(
128            target: "net::protocol_seed::start",
129            "Received {} addrs from {}", addrs_msg.addrs.len(), self.channel.display_address(),
130        );
131
132        if !addrs_msg.addrs.is_empty() {
133            debug!(
134                target: "net::protocol_seed::start",
135                "Appending to greylist...",
136            );
137            self.hosts.insert(HostColor::Grey, &addrs_msg.addrs).await;
138        }
139
140        debug!(target: "net::protocol_seed::start", "END => address={}", self.channel.display_address());
141        Ok(())
142    }
143
144    fn name(&self) -> &'static str {
145        PROTO_NAME
146    }
147}