1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use prometheus_client::registry::Registry;
use std::collections::HashSet;
use std::fs;
use std::path::PathBuf;
use subspace_networking::libp2p::kad::Mode;
use subspace_networking::libp2p::{identity, Multiaddr};
use subspace_networking::utils::strip_peer_id;
use subspace_networking::{
    CreationError, KademliaMode, KnownPeersManager, KnownPeersManagerConfig,
    KnownPeersManagerPersistenceError, Node, NodeRunner, PieceByIndexRequestHandler,
    SegmentHeaderBySegmentIndexesRequestHandler,
};
use thiserror::Error;
use tracing::{error, trace};

/// Size of the LRU cache for peers.
pub const KNOWN_PEERS_CACHE_SIZE: u32 = 100;

/// Errors that might happen during DSN configuration.
#[derive(Debug, Error)]
pub enum DsnConfigurationError {
    /// Can't instantiate the DSN.
    #[error("Can't instantiate the DSN: {0}")]
    CreationError(#[from] CreationError),
    /// Network parameter manager error.
    #[error("Network parameter manager error: {0}")]
    NetworkParameterManagerError(#[from] KnownPeersManagerPersistenceError),
}

/// DSN configuration parameters.
#[derive(Clone, Debug)]
pub struct DsnConfig {
    /// Where local DSN node will listen for incoming connections.
    pub listen_on: Vec<Multiaddr>,

    /// Bootstrap nodes for DSN.
    pub bootstrap_nodes: Vec<Multiaddr>,

    /// Reserved nodes for DSN.
    pub reserved_peers: Vec<Multiaddr>,

    /// Identity keypair of a node used for authenticated connections.
    pub keypair: identity::Keypair,

    /// Determines whether we allow keeping non-global (private, shared, loopback..) addresses in Kademlia DHT.
    pub allow_non_global_addresses_in_dht: bool,

    /// System base path.
    pub network_path: PathBuf,

    /// Defines max established incoming swarm connection limit.
    pub max_in_connections: u32,

    /// Defines max established outgoing swarm connection limit.
    pub max_out_connections: u32,

    /// Defines max pending incoming swarm connection limit.
    pub max_pending_in_connections: u32,

    /// Defines max pending outgoing swarm connection limit.
    pub max_pending_out_connections: u32,

    /// Known external addresses
    pub external_addresses: Vec<Multiaddr>,
}

pub(crate) fn create_dsn_instance(
    dsn_protocol_version: String,
    dsn_config: DsnConfig,
    prometheus_registry: Option<&mut Registry>,
) -> Result<(Node, NodeRunner<()>), DsnConfigurationError> {
    trace!("Subspace networking starting.");

    let known_peers_registry = {
        let network_path = dsn_config.network_path;

        if !network_path.is_dir() {
            fs::create_dir(&network_path)
                .map_err(|error| DsnConfigurationError::CreationError(CreationError::Io(error)))?;
        }
        let file_path = network_path.join("known_addresses.bin");

        KnownPeersManager::new(KnownPeersManagerConfig {
            path: Some(file_path.into_boxed_path()),
            ignore_peer_list: strip_peer_id(dsn_config.bootstrap_nodes.clone())
                .into_iter()
                .map(|(peer_id, _)| peer_id)
                .collect::<HashSet<_>>(),
            cache_size: KNOWN_PEERS_CACHE_SIZE,
            ..Default::default()
        })
        .map(KnownPeersManager::boxed)?
    };

    let keypair = dsn_config.keypair.clone();
    let default_networking_config =
        subspace_networking::Config::new(dsn_protocol_version, keypair, (), prometheus_registry);

    let networking_config = subspace_networking::Config {
        keypair: dsn_config.keypair.clone(),
        listen_on: dsn_config.listen_on,
        allow_non_global_addresses_in_dht: dsn_config.allow_non_global_addresses_in_dht,
        known_peers_registry,
        request_response_protocols: vec![
            // We need to enable protocol to request pieces
            PieceByIndexRequestHandler::create(|_, _| async { None }),
            SegmentHeaderBySegmentIndexesRequestHandler::create(move |_, _| async move { None }),
        ],
        max_established_incoming_connections: dsn_config.max_in_connections,
        max_established_outgoing_connections: dsn_config.max_out_connections,
        max_pending_incoming_connections: dsn_config.max_pending_in_connections,
        max_pending_outgoing_connections: dsn_config.max_pending_out_connections,
        reserved_peers: dsn_config.reserved_peers,
        bootstrap_addresses: dsn_config.bootstrap_nodes,
        external_addresses: dsn_config.external_addresses,
        kademlia_mode: KademliaMode::Static(Mode::Client),

        ..default_networking_config
    };

    subspace_networking::construct(networking_config).map_err(Into::into)
}