kyoto/
builder.rs

1use std::net::{IpAddr, SocketAddr};
2use std::{path::PathBuf, time::Duration};
3
4use bitcoin::Network;
5
6use super::{client::Client, config::NodeConfig, node::Node};
7#[cfg(feature = "rusqlite")]
8use crate::db::error::SqlInitializationError;
9#[cfg(feature = "rusqlite")]
10use crate::db::sqlite::{headers::SqliteHeaderDb, peers::SqlitePeerDb};
11use crate::network::dns::{DnsResolver, DNS_RESOLVER_PORT};
12use crate::network::ConnectionType;
13use crate::{
14    chain::checkpoints::HeaderCheckpoint,
15    db::traits::{HeaderStore, PeerStore},
16};
17use crate::{PeerStoreSizeConfig, TrustedPeer};
18
19#[cfg(feature = "rusqlite")]
20/// The default node returned from the [`Builder`].
21pub type NodeDefault = Node<SqliteHeaderDb, SqlitePeerDb>;
22
23const MIN_PEERS: u8 = 1;
24const MAX_PEERS: u8 = 15;
25
26/// Build a [`Node`] in an additive way.
27///
28/// # Examples
29///
30/// Nodes may be built with minimal configuration.
31///
32/// ```rust
33/// use std::net::{IpAddr, Ipv4Addr};
34/// use std::collections::HashSet;
35/// use kyoto::{Builder, Network};
36///
37/// let host = (IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), None);
38/// let builder = Builder::new(Network::Regtest);
39/// let (node, client) = builder
40///     .add_peers(vec![host.into()])
41///     .build()
42///     .unwrap();
43/// ```
44pub struct Builder {
45    config: NodeConfig,
46    network: Network,
47}
48
49impl Builder {
50    /// Create a new [`Builder`].
51    pub fn new(network: Network) -> Self {
52        Self {
53            config: NodeConfig::default(),
54            network,
55        }
56    }
57
58    /// Fetch the [`Network`] for the builder.
59    pub fn network(&self) -> Network {
60        self.network
61    }
62
63    /// Add preferred peers to try to connect to.
64    pub fn add_peers(mut self, whitelist: impl IntoIterator<Item = TrustedPeer>) -> Self {
65        self.config.white_list.extend(whitelist);
66        self
67    }
68
69    /// Add a preferred peer to try to connect to.
70    pub fn add_peer(mut self, trusted_peer: impl Into<TrustedPeer>) -> Self {
71        self.config.white_list.push(trusted_peer.into());
72        self
73    }
74
75    /// Add a path to the directory where data should be stored. If none is provided, the current
76    /// working directory will be used.
77    pub fn data_dir(mut self, path: impl Into<PathBuf>) -> Self {
78        self.config.data_path = Some(path.into());
79        self
80    }
81
82    /// Add the minimum number of peer connections that should be maintained by the node.
83    /// Adding more connections increases the node's anonymity, but requires waiting for more responses,
84    /// higher bandwidth, and higher memory requirements. If none is provided, a single connection will be maintained.
85    /// The number of connections will be clamped to a range of 1 to 15.
86    pub fn required_peers(mut self, num_peers: u8) -> Self {
87        self.config.required_peers = num_peers.clamp(MIN_PEERS, MAX_PEERS);
88        self
89    }
90
91    /// Set the desired number of peers for the database to keep track of. For limited or in-memory peer storage,
92    /// this number may be small, however a sufficient margin of peers should be set so the node can try many options
93    /// when downloading compact block filters. For nodes that store peers on disk, more peers will typically result in
94    /// fewer errors. If none is provided, no limit to the size of the store will be introduced.
95    pub fn peer_db_size(mut self, target: PeerStoreSizeConfig) -> Self {
96        self.config.target_peer_size = target;
97        self
98    }
99
100    /// Add a checkpoint for the node to look for relevant blocks _strictly after_ the given height.
101    /// This may be from the same [`HeaderCheckpoint`] every time the node is ran, or from the last known sync height.
102    /// In the case of a block reorganization, the node may scan for blocks below the given block height
103    /// to accurately reflect which relevant blocks are in the best chain.
104    /// If none is provided, the _most recent_ checkpoint will be used.
105    pub fn after_checkpoint(mut self, checkpoint: impl Into<HeaderCheckpoint>) -> Self {
106        self.config.header_checkpoint = Some(checkpoint.into());
107        self
108    }
109
110    /// Set the time a peer has to complete the initial TCP handshake. Even on unstable
111    /// connections this may be fast.
112    ///
113    /// If none is provided, a timeout of two seconds will be used.
114    pub fn handshake_timeout(mut self, handshake_timeout: impl Into<Duration>) -> Self {
115        self.config.peer_timeout_config.handshake_timeout = handshake_timeout.into();
116        self
117    }
118
119    /// Set the time duration a peer has to respond to a message from the local node.
120    ///
121    /// ## Note
122    ///
123    /// Both bandwidth and computing time should be considered when configuring this timeout.
124    /// On test networks, this value may be quite short, however on the Bitcoin network,
125    /// nodes may be slower to respond while processing blocks and transactions.
126    ///
127    /// If none is provided, a timeout of 5 seconds will be used.
128    pub fn response_timeout(mut self, response_timeout: impl Into<Duration>) -> Self {
129        self.config.peer_timeout_config.response_timeout = response_timeout.into();
130        self
131    }
132
133    /// The maximum connection time that will be maintained with a remote peer, regardless of
134    /// the quality of the peer.
135    ///
136    /// ## Note
137    ///
138    /// This value is configurable as some developers may be satisfied with a peer
139    /// as long as the peer responds promptly. Other implementations may value finding
140    /// new and reliable peers faster, so the maximum connection time may be shorter.
141    ///
142    /// If none is provided, a maximum connection time of two hours will be used.
143    pub fn maximum_connection_time(mut self, max_connection_time: impl Into<Duration>) -> Self {
144        self.config.peer_timeout_config.max_connection_time = max_connection_time.into();
145        self
146    }
147
148    /// Configure the DNS resolver to use when querying DNS seeds.
149    /// Default is `1.1.1.1:53`.
150    pub fn dns_resolver(mut self, resolver: impl Into<IpAddr>) -> Self {
151        let ip_addr = resolver.into();
152        let socket_addr = SocketAddr::new(ip_addr, DNS_RESOLVER_PORT);
153        self.config.dns_resolver = DnsResolver { socket_addr };
154        self
155    }
156
157    /// Route network traffic through a Tor daemon using a Socks5 proxy. Currently, proxies
158    /// must be reachable by IP address.
159    pub fn socks5_proxy(mut self, proxy: impl Into<SocketAddr>) -> Self {
160        let ip_addr = proxy.into();
161        let connection = ConnectionType::Socks5Proxy(ip_addr);
162        self.config.connection_type = connection;
163        self
164    }
165
166    /// Consume the node builder and receive a [`Node`] and [`Client`].
167    ///
168    /// # Errors
169    ///
170    /// Building a node and client will error if a database connection is denied or cannot be found.
171    #[cfg(feature = "rusqlite")]
172    pub fn build(&mut self) -> Result<(NodeDefault, Client), SqlInitializationError> {
173        let peer_store = SqlitePeerDb::new(self.network, self.config.data_path.clone())?;
174        let header_store = SqliteHeaderDb::new(self.network, self.config.data_path.clone())?;
175        Ok(Node::new(
176            self.network,
177            core::mem::take(&mut self.config),
178            peer_store,
179            header_store,
180        ))
181    }
182
183    /// Consume the node builder by using custom database implementations, receiving a [`Node`] and [`Client`].
184    pub fn build_with_databases<H: HeaderStore + 'static, P: PeerStore + 'static>(
185        &mut self,
186        peer_store: P,
187        header_store: H,
188    ) -> (Node<H, P>, Client) {
189        Node::new(
190            self.network,
191            core::mem::take(&mut self.config),
192            peer_store,
193            header_store,
194        )
195    }
196}