bitcoin_peers_connection/
peer.rs

1//! Bitcoin peer information structures and utilities.
2
3use bitcoin::p2p::address::AddrV2;
4use bitcoin::p2p::ServiceFlags;
5use std::fmt;
6
7/// Minimum protocol version for basic compatibility with modern bitcoin nodes.
8pub const MIN_PROTOCOL_VERSION: u32 = 70001;
9/// Minimum protocol version that supports AddrV2 messages (BIP155).
10///
11/// Bitcoin Core implemented this in version 0.21.0 with protocol version 70016.
12pub const ADDRV2_MIN_PROTOCOL_VERSION: u32 = 70016;
13/// Minimum protocol version that supports SendHeaders.
14///
15/// Bitcoin Core implemented this in version 0.12.0 with protocol version 70012.
16pub const SENDHEADERS_MIN_PROTOCOL_VERSION: u32 = 70012;
17/// Minimum protocol version that supports WtxidRelay.
18///
19/// Bitcoin Core implemented this in version 0.21.0 with protocol version 70016.
20pub const WTXID_RELAY_MIN_PROTOCOL_VERSION: u32 = 70016;
21
22/// Represents the service state of a peer.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum PeerServices {
25    /// Known services with specific ServiceFlags.
26    Known(ServiceFlags),
27    /// Unknown services state.
28    Unknown,
29}
30
31/// Represents the protocol version of a peer.
32///
33/// * **70001** - BIP 0031, absolute minimum for modern nodes.
34/// * **70002** - BIP 0035, added mempool message.
35/// * **70012** - BIP 0065, added CheckLockTimeVerify.
36/// * **70013** - BIP 0130/BIP 0133, added sendheaders and feefilter.
37/// * **70014** - BIP 0152, added compact blocks.
38/// * **70015** - BIP 0141/BIP 0143/BIP 0147, SegWit support.
39/// * **70016** - BIP 157/158, coompact block filters.
40///
41/// Nodes running Bitcoin Core 0.10.0 and later typically reject connections from peers
42/// with protocol versions below 70001. If you use a lower version, you'll likely be
43/// unable to connect to most of the network.
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub enum PeerProtocolVersion {
46    /// Known protocol version.
47    Known(u32),
48    /// Unknown protocol version.
49    Unknown,
50}
51
52impl PeerProtocolVersion {
53    /// Returns the protocol version value if known, or a default value if unknown.
54    ///
55    /// # Arguments
56    ///
57    /// * `default` - The default value to return if the version is unknown.
58    ///
59    /// # Returns
60    ///
61    /// The version number if known, or the provided default value.
62    pub fn unwrap_or(self, default: u32) -> u32 {
63        match self {
64            PeerProtocolVersion::Known(v) => v,
65            PeerProtocolVersion::Unknown => default,
66        }
67    }
68}
69
70impl fmt::Display for PeerProtocolVersion {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self {
73            PeerProtocolVersion::Known(version) => write!(f, "Known({version})"),
74            PeerProtocolVersion::Unknown => write!(f, "Unknown"),
75        }
76    }
77}
78
79/// Represents a bitcoin peer on the network.
80#[derive(Debug, Clone, PartialEq, Eq, Hash)]
81pub struct Peer {
82    /// The peer's network address.
83    pub address: AddrV2,
84    /// The port number the peer is listening on.
85    pub port: u16,
86    /// The service flags advertised by the peer.
87    pub services: PeerServices,
88    /// The protocol version of the peer.
89    pub version: PeerProtocolVersion,
90}
91
92impl Peer {
93    /// Create a new peer with unknown services and version.
94    pub fn new(address: AddrV2, port: u16) -> Self {
95        Peer {
96            address,
97            port,
98            services: PeerServices::Unknown,
99            version: PeerProtocolVersion::Unknown,
100        }
101    }
102
103    /// Create a new peer with known services.
104    pub fn with_services(address: AddrV2, port: u16, services: ServiceFlags) -> Self {
105        Peer {
106            address,
107            port,
108            services: PeerServices::Known(services),
109            version: PeerProtocolVersion::Unknown,
110        }
111    }
112
113    /// Checks if the peer advertises the specified service.
114    ///
115    /// # Arguments
116    ///
117    /// * `service` - The service flag to check for.
118    ///
119    /// # Returns
120    ///
121    /// `true` if the peer advertises the service, `false` otherwise.
122    pub fn has_service(&self, service: ServiceFlags) -> bool {
123        match self.services {
124            PeerServices::Known(flags) => flags.has(service),
125            PeerServices::Unknown => false,
126        }
127    }
128
129    /// Returns a new Peer with known services.
130    pub fn with_known_services(&self, services: ServiceFlags) -> Self {
131        Peer {
132            address: self.address.clone(),
133            port: self.port,
134            services: PeerServices::Known(services),
135            version: self.version,
136        }
137    }
138
139    /// Returns a new Peer with known version.
140    pub fn with_known_version(&self, version: u32) -> Self {
141        Peer {
142            address: self.address.clone(),
143            port: self.port,
144            services: self.services,
145            version: PeerProtocolVersion::Known(version),
146        }
147    }
148}
149
150impl fmt::Display for Peer {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        write!(
153            f,
154            "{:?}:{} ([peer] services: {}, version: {})",
155            self.address,
156            self.port,
157            match self.services {
158                PeerServices::Known(flags) => flags.to_string(),
159                PeerServices::Unknown => "unknown".to_string(),
160            },
161            match self.version {
162                PeerProtocolVersion::Known(v) => v.to_string(),
163                PeerProtocolVersion::Unknown => "unknown".to_string(),
164            }
165        )
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172    use std::net::Ipv4Addr;
173
174    #[test]
175    fn test_peer_with_services() {
176        let addr = AddrV2::Ipv4(Ipv4Addr::new(127, 0, 0, 1));
177        let port = 8333;
178        let services = ServiceFlags::NETWORK;
179
180        let peer = Peer::with_services(addr.clone(), port, services);
181
182        assert_eq!(peer.address, addr);
183        assert_eq!(peer.port, port);
184        assert_eq!(peer.services, PeerServices::Known(services));
185        assert_eq!(peer.version, PeerProtocolVersion::Unknown);
186        assert!(peer.has_service(ServiceFlags::NETWORK));
187    }
188
189    #[test]
190    fn test_peer_constructors_equivalence() {
191        let addr = AddrV2::Ipv4(Ipv4Addr::new(192, 168, 1, 1));
192        let port = 8333;
193        let services = ServiceFlags::NETWORK | ServiceFlags::WITNESS;
194
195        // These should create equivalent peers
196        let peer1 = Peer::with_services(addr.clone(), port, services);
197        let peer2 = Peer::new(addr.clone(), port).with_known_services(services);
198
199        assert_eq!(peer1, peer2);
200    }
201}