kyoto/
lib.rs

1//! Kyoto is a conservative, private, and vetted Bitcoin client built in accordance
2//! with the [BIP157](https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki) and [BIP158](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki)
3//! standards. _Conservative_, as in Kyoto makes very little assumptions about the underlying memory requirements of the
4//! device running the software. _Private_, as in the Bitcoin nodes that serve Kyoto nodes data do not know what transactions the
5//! client is querying for, only the entire Bitcoin block. _Vetted_, as in the dependencies of the core library are meant to remain limited,
6//! rigorously tested, and absolutely necessary.
7//!
8//! # Example usage
9//!
10//! ```no_run
11//! use kyoto::{Builder, Event, Client, Network, BlockHash};
12//!
13//! #[tokio::main]
14//! async fn main() {
15//!     // Add third-party logging
16//!     let subscriber = tracing_subscriber::FmtSubscriber::new();
17//!     tracing::subscriber::set_global_default(subscriber).unwrap();
18//!     // Create a new node builder
19//!     let builder = Builder::new(Network::Signet);
20//!     // Add node preferences and build the node/client
21//!     let (node, client) = builder
22//!         // The number of connections we would like to maintain
23//!         .required_peers(2)
24//!         .build()
25//!         .unwrap();
26//!     // Run the node and wait for the sync message;
27//!     tokio::task::spawn(async move { node.run().await });
28//!     // Split the client into components that send messages and listen to messages
29//!     let Client { requester, info_rx: _, warn_rx: _, mut event_rx } = client;
30//!     loop {
31//!         if let Some(event) = event_rx.recv().await {
32//!             match event {
33//!                 Event::FiltersSynced(_) => {
34//!                     tracing::info!("Sync complete!");
35//!                     break;
36//!                 },
37//!                 _ => (),
38//!             }
39//!         }
40//!     }
41//!     requester.shutdown();
42//! }
43//! ```
44//!
45//! # Features
46//!
47//! `rusqlite`: use the default `rusqlite` database implementations. Default and recommend feature.
48
49#![warn(missing_docs)]
50pub mod chain;
51pub mod db;
52
53mod network;
54mod prelude;
55use network::dns::CBF_SERVICE_BIT_PREFIX;
56use network::dns::CBF_V2T_SERVICE_BIT_PREFIX;
57pub(crate) use prelude::impl_sourceless_error;
58
59mod broadcaster;
60/// Convenient way to build a compact filters node.
61pub mod builder;
62pub(crate) mod channel_messages;
63/// Structures to communicate with a node.
64pub mod client;
65/// Node configuration options.
66pub(crate) mod config;
67pub(crate) mod dialog;
68/// Errors associated with a node.
69pub mod error;
70/// Messages the node may send a client.
71pub mod messages;
72/// The structure that communicates with the Bitcoin P2P network and collects data.
73pub mod node;
74
75use chain::Filter;
76
77use network::dns::DnsQuery;
78use network::dns::DNS_RESOLVER_PORT;
79
80use std::net::{IpAddr, SocketAddr};
81
82// Re-exports
83#[doc(inline)]
84pub use chain::checkpoints::HeaderCheckpoint;
85
86#[doc(inline)]
87#[cfg(feature = "rusqlite")]
88pub use db::sqlite::{headers::SqliteHeaderDb, peers::SqlitePeerDb};
89
90#[doc(inline)]
91pub use db::traits::{HeaderStore, PeerStore};
92
93#[doc(inline)]
94pub use tokio::sync::mpsc::Receiver;
95#[doc(inline)]
96pub use tokio::sync::mpsc::UnboundedReceiver;
97
98#[doc(inline)]
99pub use {
100    crate::builder::Builder,
101    crate::client::{Client, Requester},
102    crate::error::{ClientError, NodeError},
103    crate::messages::{Event, Info, Progress, RejectPayload, SyncUpdate, Warning},
104    crate::network::PeerTimeoutConfig,
105    crate::node::Node,
106};
107
108#[doc(inline)]
109pub use bitcoin::bip158::BlockFilter;
110#[doc(inline)]
111pub use bitcoin::{
112    block::Header, p2p::address::AddrV2, p2p::message_network::RejectReason, p2p::ServiceFlags,
113    Address, Block, BlockHash, FeeRate, Network, ScriptBuf, Transaction, Txid,
114};
115
116pub extern crate tokio;
117
118/// A Bitcoin [`Block`] with associated height.
119#[derive(Debug, Clone)]
120pub struct IndexedBlock {
121    /// The height or index in the chain.
122    pub height: u32,
123    /// The Bitcoin block with some matching script.
124    pub block: Block,
125}
126
127impl IndexedBlock {
128    pub(crate) fn new(height: u32, block: Block) -> Self {
129        Self { height, block }
130    }
131}
132
133/// A compact block filter with associated height.
134#[derive(Debug, Clone, PartialEq, Eq)]
135pub struct IndexedFilter {
136    height: u32,
137    filter: Filter,
138}
139
140impl IndexedFilter {
141    fn new(height: u32, filter: Filter) -> Self {
142        Self { height, filter }
143    }
144
145    /// The height in the chain.
146    pub fn height(&self) -> u32 {
147        self.height
148    }
149
150    /// Return the [`BlockHash`] associated with this filer
151    pub fn block_hash(&self) -> BlockHash {
152        *self.filter.block_hash()
153    }
154
155    /// Does the filter contain a positive match for any of the provided scripts
156    pub fn contains_any<'a>(&'a self, scripts: impl Iterator<Item = &'a ScriptBuf>) -> bool {
157        self.filter
158            .contains_any(scripts)
159            .expect("vec reader is infallible")
160    }
161
162    /// Consume the index and get underlying block filter.
163    pub fn block_filter(self) -> BlockFilter {
164        self.filter.into_filter()
165    }
166
167    /// Consume the filter and get the raw bytes
168    pub fn into_contents(self) -> Vec<u8> {
169        self.filter.contents()
170    }
171}
172
173impl std::cmp::PartialOrd for IndexedFilter {
174    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
175        Some(self.cmp(other))
176    }
177}
178
179impl std::cmp::Ord for IndexedFilter {
180    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
181        self.height.cmp(&other.height)
182    }
183}
184
185/// Broadcast a [`Transaction`] to a set of connected peers.
186#[derive(Debug, Clone)]
187pub struct TxBroadcast {
188    /// The presumably valid Bitcoin transaction.
189    pub tx: Transaction,
190    /// The strategy for how this transaction should be shared with the network.
191    pub broadcast_policy: TxBroadcastPolicy,
192}
193
194impl TxBroadcast {
195    /// Prepare a transaction for broadcast with associated broadcast strategy.
196    pub fn new(tx: Transaction, broadcast_policy: TxBroadcastPolicy) -> Self {
197        Self {
198            tx,
199            broadcast_policy,
200        }
201    }
202
203    /// Prepare a transaction to be broadcasted to a random connection.
204    pub fn random_broadcast(tx: Transaction) -> Self {
205        Self {
206            tx,
207            broadcast_policy: TxBroadcastPolicy::RandomPeer,
208        }
209    }
210}
211
212/// The strategy for how this transaction should be shared with the network.
213#[derive(Debug, Default, Clone)]
214pub enum TxBroadcastPolicy {
215    /// Broadcast the transaction to all peers at the same time.
216    AllPeers,
217    /// Broadcast the transaction to a single random peer, optimal for user privacy.
218    #[default]
219    RandomPeer,
220}
221
222/// A peer on the Bitcoin P2P network
223///
224/// # Building peers
225///
226/// ```rust
227/// use std::net::{IpAddr, Ipv4Addr};
228/// use kyoto::{TrustedPeer, ServiceFlags, AddrV2};
229///
230/// let local_host = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
231/// let mut trusted = TrustedPeer::from_ip(local_host);
232/// // Optionally set the known services of the peer later.
233/// trusted.set_services(ServiceFlags::P2P_V2);
234///
235/// let local_host = Ipv4Addr::new(0, 0, 0, 0);
236/// // Or construct a trusted peer directly.
237/// let trusted = TrustedPeer::new(AddrV2::Ipv4(local_host), None, ServiceFlags::P2P_V2);
238///
239/// // Or implicitly with `into`
240/// let local_host = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
241/// let trusted: TrustedPeer = (local_host, None).into();
242/// ```
243#[derive(Debug, Clone)]
244pub struct TrustedPeer {
245    /// The IP address of the remote node to connect to.
246    pub address: AddrV2,
247    /// The port to establish a TCP connection. If none is provided, the typical Bitcoin Core port is used as the default.
248    pub port: Option<u16>,
249    /// The services this peer is known to offer before starting the node.
250    pub known_services: ServiceFlags,
251}
252
253impl TrustedPeer {
254    /// Create a new trusted peer.
255    pub fn new(address: AddrV2, port: Option<u16>, services: ServiceFlags) -> Self {
256        Self {
257            address,
258            port,
259            known_services: services,
260        }
261    }
262
263    /// Create a new trusted peer using the default port for the network.
264    pub fn from_ip(ip_addr: impl Into<IpAddr>) -> Self {
265        let address = match ip_addr.into() {
266            IpAddr::V4(ip) => AddrV2::Ipv4(ip),
267            IpAddr::V6(ip) => AddrV2::Ipv6(ip),
268        };
269        Self {
270            address,
271            port: None,
272            known_services: ServiceFlags::NONE,
273        }
274    }
275
276    /// Create a new peer from a known address and port.
277    pub fn from_socket_addr(socket_addr: impl Into<SocketAddr>) -> Self {
278        let socket_addr: SocketAddr = socket_addr.into();
279        let address = match socket_addr {
280            SocketAddr::V4(ip) => AddrV2::Ipv4(*ip.ip()),
281            SocketAddr::V6(ip) => AddrV2::Ipv6(*ip.ip()),
282        };
283        Self {
284            address,
285            port: Some(socket_addr.port()),
286            known_services: ServiceFlags::NONE,
287        }
288    }
289
290    /// The IP address of the trusted peer.
291    pub fn address(&self) -> AddrV2 {
292        self.address.clone()
293    }
294
295    /// A recommended port to connect to, if there is one.
296    pub fn port(&self) -> Option<u16> {
297        self.port
298    }
299
300    /// The services this peer is known to offer.
301    pub fn services(&self) -> ServiceFlags {
302        self.known_services
303    }
304
305    /// Set the known services for this trusted peer.
306    pub fn set_services(&mut self, services: ServiceFlags) {
307        self.known_services = services;
308    }
309}
310
311impl From<(IpAddr, Option<u16>)> for TrustedPeer {
312    fn from(value: (IpAddr, Option<u16>)) -> Self {
313        let address = match value.0 {
314            IpAddr::V4(ip) => AddrV2::Ipv4(ip),
315            IpAddr::V6(ip) => AddrV2::Ipv6(ip),
316        };
317        TrustedPeer::new(address, value.1, ServiceFlags::NONE)
318    }
319}
320
321impl From<TrustedPeer> for (AddrV2, Option<u16>) {
322    fn from(value: TrustedPeer) -> Self {
323        (value.address(), value.port())
324    }
325}
326
327impl From<IpAddr> for TrustedPeer {
328    fn from(value: IpAddr) -> Self {
329        TrustedPeer::from_ip(value)
330    }
331}
332
333impl From<SocketAddr> for TrustedPeer {
334    fn from(value: SocketAddr) -> Self {
335        TrustedPeer::from_socket_addr(value)
336    }
337}
338
339/// Configure how many peers will be stored.
340#[derive(Debug, Default, Clone)]
341pub enum PeerStoreSizeConfig {
342    /// Add new peers to the store regardless of the current size. For memory-limited [`PeerStore`]
343    /// implementations, consider using a bounded size.
344    #[default]
345    Unbounded,
346    /// Bound the size of the [`PeerStore`]. When set, no new peers will be requested if the database
347    /// has at least this amount of peers.
348    Limit(u32),
349}
350
351// The state of the node with respect to connected peers.
352#[derive(Debug, Clone, Copy)]
353enum NodeState {
354    /// We are behind on block headers according to our peers.
355    Behind,
356    /// We may start downloading compact block filter headers.
357    HeadersSynced,
358    /// We may start scanning compact block filters.
359    FilterHeadersSynced,
360    /// We may start asking for blocks with matches.
361    FiltersSynced,
362}
363
364impl core::fmt::Display for NodeState {
365    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
366        match self {
367            NodeState::Behind => {
368                write!(f, "Requesting block headers.")
369            }
370            NodeState::HeadersSynced => {
371                write!(f, "Requesting compact filter headers.")
372            }
373            NodeState::FilterHeadersSynced => {
374                write!(f, "Requesting compact block filters.")
375            }
376            NodeState::FiltersSynced => write!(f, "Downloading blocks with relevant transactions."),
377        }
378    }
379}
380
381/// Query a Bitcoin DNS seeder using the configured resolver.
382///
383/// This is **not** a generic DNS implementation. It is specifically tailored to query and parse DNS for Bitcoin seeders.
384/// Iternally, three queries will be made with varying filters for the requested service flags.
385/// Similar to [`lookup_host`](tokio::net::lookup_host), this has no guarantee to return any `IpAddr`.
386///
387/// # Example usage
388///
389/// ```no_run
390/// use std::net::{IpAddr, Ipv4Addr};
391///
392/// use kyoto::lookup_host;
393///
394/// #[tokio::main]
395/// async fn main() {
396///     // Use cloudflare's DNS resolver
397///     let resolver = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
398///     let addrs = lookup_host("seed.bitcoin.sipa.be", resolver).await;
399/// }
400/// ```
401pub async fn lookup_host<S: AsRef<str>>(hostname: S, resolver: impl Into<IpAddr>) -> Vec<IpAddr> {
402    let ip_addr = resolver.into();
403    let socket_addr = SocketAddr::new(ip_addr, DNS_RESOLVER_PORT);
404    let mut responses = Vec::new();
405    let dns_query = DnsQuery::new(hostname.as_ref(), None);
406    let no_filter = dns_query.lookup(socket_addr).await.unwrap_or(Vec::new());
407    responses.extend(no_filter);
408    let dns_query = DnsQuery::new(hostname.as_ref(), Some(CBF_V2T_SERVICE_BIT_PREFIX));
409    let no_filter = dns_query.lookup(socket_addr).await.unwrap_or(Vec::new());
410    responses.extend(no_filter);
411    let dns_query = DnsQuery::new(hostname.as_ref(), Some(CBF_SERVICE_BIT_PREFIX));
412    let no_filter = dns_query.lookup(socket_addr).await.unwrap_or(Vec::new());
413    responses.extend(no_filter);
414    responses
415}
416
417macro_rules! debug {
418    ($expr:expr) => {
419        #[cfg(debug_assertions)]
420        println!("{}", $expr)
421    };
422}
423
424pub(crate) use debug;