bitcoin/
network.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Bitcoin network.
4//!
5//! The term "network" is overloaded, here [`Network`] refers to the specific
6//! Bitcoin network we are operating on e.g., signet, regtest. The terms
7//! "network" and "chain" are often used interchangeably for this concept.
8//!
9//! # Example: encoding a network's magic bytes
10//!
11//! ```rust
12//! use bitcoin::Network;
13//! use bitcoin::consensus::encode::serialize;
14//!
15//! let network = Network::Bitcoin;
16//! let bytes = serialize(&network.magic());
17//!
18//! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD9]);
19//! ```
20
21use core::fmt;
22use core::fmt::Display;
23use core::str::FromStr;
24
25use internals::write_err;
26#[cfg(feature = "serde")]
27use serde::{Deserialize, Serialize};
28
29use crate::consensus::Params;
30use crate::constants::ChainHash;
31use crate::p2p::Magic;
32use crate::prelude::*;
33
34/// What kind of network we are on.
35#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub enum NetworkKind {
37    /// The Bitcoin mainnet network.
38    Main,
39    /// Some kind of testnet network.
40    Test,
41}
42
43// We explicitly do not provide `is_testnet`, using `!network.is_mainnet()` is less
44// ambiguous due to confusion caused by signet/testnet/regtest.
45impl NetworkKind {
46    /// Returns true if this is real mainnet bitcoin.
47    pub fn is_mainnet(&self) -> bool { *self == NetworkKind::Main }
48}
49
50impl From<Network> for NetworkKind {
51    fn from(n: Network) -> Self {
52        use Network::*;
53
54        match n {
55            Bitcoin => NetworkKind::Main,
56            Testnet | Testnet4 | Signet | Regtest => NetworkKind::Test,
57        }
58    }
59}
60
61/// The cryptocurrency network to act on.
62#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
65#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
66#[non_exhaustive]
67pub enum Network {
68    /// Mainnet Bitcoin.
69    Bitcoin,
70    /// Bitcoin's testnet network. (In future versions this will be combined
71    /// into a single variant containing the version)
72    Testnet,
73    /// Bitcoin's testnet4 network. (In future versions this will be combined
74    /// into a single variant containing the version)
75    Testnet4,
76    /// Bitcoin's signet network.
77    Signet,
78    /// Bitcoin's regtest network.
79    Regtest,
80}
81
82impl Network {
83    /// Creates a `Network` from the magic bytes.
84    ///
85    /// # Examples
86    ///
87    /// ```rust
88    /// use bitcoin::p2p::Magic;
89    /// use bitcoin::Network;
90    ///
91    /// assert_eq!(Ok(Network::Bitcoin), Network::try_from(Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9])));
92    /// assert_eq!(None, Network::from_magic(Magic::from_bytes([0xFF, 0xFF, 0xFF, 0xFF])));
93    /// ```
94    pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
95
96    /// Return the network magic bytes, which should be encoded little-endian
97    /// at the start of every message
98    ///
99    /// # Examples
100    ///
101    /// ```rust
102    /// use bitcoin::p2p::Magic;
103    /// use bitcoin::Network;
104    ///
105    /// let network = Network::Bitcoin;
106    /// assert_eq!(network.magic(), Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9]));
107    /// ```
108    pub fn magic(self) -> Magic { Magic::from(self) }
109
110    /// Converts a `Network` to its equivalent `bitcoind -chain` argument name.
111    ///
112    /// ```bash
113    /// $ bitcoin-23.0/bin/bitcoind --help | grep -C 3 '\-chain=<chain>'
114    /// Chain selection options:
115    ///
116    /// -chain=<chain>
117    /// Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest
118    /// ```
119    pub fn to_core_arg(self) -> &'static str {
120        match self {
121            Network::Bitcoin => "main",
122            // For user-side compatibility, testnet3 is retained as test
123            Network::Testnet => "test",
124            Network::Testnet4 => "testnet4",
125            Network::Signet => "signet",
126            Network::Regtest => "regtest",
127        }
128    }
129
130    /// Converts a `bitcoind -chain` argument name to its equivalent `Network`.
131    ///
132    /// ```bash
133    /// $ bitcoin-23.0/bin/bitcoind --help | grep -C 3 '\-chain=<chain>'
134    /// Chain selection options:
135    ///
136    /// -chain=<chain>
137    /// Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest
138    /// ```
139    pub fn from_core_arg(core_arg: &str) -> Result<Self, ParseNetworkError> {
140        use Network::*;
141
142        let network = match core_arg {
143            "main" => Bitcoin,
144            "test" => Testnet,
145            "testnet4" => Testnet4,
146            "signet" => Signet,
147            "regtest" => Regtest,
148            _ => return Err(ParseNetworkError(core_arg.to_owned())),
149        };
150        Ok(network)
151    }
152
153    /// Return the network's chain hash (genesis block hash).
154    ///
155    /// # Examples
156    ///
157    /// ```rust
158    /// use bitcoin::Network;
159    /// use bitcoin::blockdata::constants::ChainHash;
160    ///
161    /// let network = Network::Bitcoin;
162    /// assert_eq!(network.chain_hash(), ChainHash::BITCOIN);
163    /// ```
164    pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block_const(self) }
165
166    /// Creates a `Network` from the chain hash (genesis block hash).
167    ///
168    /// # Examples
169    ///
170    /// ```rust
171    /// use bitcoin::Network;
172    /// use bitcoin::blockdata::constants::ChainHash;
173    ///
174    /// assert_eq!(Ok(Network::Bitcoin), Network::try_from(ChainHash::BITCOIN));
175    /// ```
176    pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> {
177        Network::try_from(chain_hash).ok()
178    }
179
180    /// Returns the associated network parameters.
181    pub const fn params(self) -> &'static Params {
182        match self {
183            Network::Bitcoin => &Params::BITCOIN,
184            Network::Testnet => &Params::TESTNET3,
185            Network::Testnet4 => &Params::TESTNET4,
186            Network::Signet => &Params::SIGNET,
187            Network::Regtest => &Params::REGTEST,
188        }
189    }
190
191    /// Returns a string representation of the `Network` enum variant.
192    /// This is useful for displaying the network type as a string.
193    const fn as_display_str(self) -> &'static str {
194        match self {
195            Network::Bitcoin => "bitcoin",
196            Network::Testnet => "testnet",
197            Network::Testnet4 => "testnet4",
198            Network::Signet => "signet",
199            Network::Regtest => "regtest",
200        }
201    }
202}
203
204#[cfg(feature = "serde")]
205pub mod as_core_arg {
206    //! Module for serialization/deserialization of network variants into/from Bitcoin Core values
207    #![allow(missing_docs)]
208
209    use crate::Network;
210
211    pub fn serialize<S>(network: &Network, serializer: S) -> Result<S::Ok, S::Error>
212    where
213        S: serde::Serializer,
214    {
215        serializer.serialize_str(network.to_core_arg())
216    }
217
218    pub fn deserialize<'de, D>(deserializer: D) -> Result<Network, D::Error>
219    where
220        D: serde::Deserializer<'de>,
221    {
222        struct NetworkVisitor;
223
224        impl<'de> serde::de::Visitor<'de> for NetworkVisitor {
225            type Value = Network;
226
227            fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
228                Network::from_core_arg(s).map_err(|_| {
229                    E::invalid_value(
230                        serde::de::Unexpected::Str(s),
231                        &"bitcoin network encoded as a string (either main, test, testnet4, signet or regtest)",
232                    )
233                })
234            }
235
236            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
237                write!(
238                    formatter,
239                    "bitcoin network encoded as a string (either main, test, testnet4, signet or regtest)"
240                )
241            }
242        }
243
244        deserializer.deserialize_str(NetworkVisitor)
245    }
246}
247
248/// An error in parsing network string.
249#[derive(Debug, Clone, PartialEq, Eq)]
250#[non_exhaustive]
251pub struct ParseNetworkError(pub(crate) String);
252
253impl fmt::Display for ParseNetworkError {
254    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
255        write_err!(f, "failed to parse {} as network", self.0; self)
256    }
257}
258
259#[cfg(feature = "std")]
260impl std::error::Error for ParseNetworkError {
261    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
262}
263
264impl FromStr for Network {
265    type Err = ParseNetworkError;
266
267    #[inline]
268    fn from_str(s: &str) -> Result<Self, Self::Err> {
269        match s {
270            "bitcoin" => Ok(Network::Bitcoin),
271            // For user-side compatibility, testnet3 is retained as testnet
272            "testnet" => Ok(Network::Testnet),
273            "testnet4" => Ok(Network::Testnet4),
274            "signet" => Ok(Network::Signet),
275            "regtest" => Ok(Network::Regtest),
276            _ => Err(ParseNetworkError(s.to_owned())),
277        }
278    }
279}
280
281impl fmt::Display for Network {
282    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
283        write!(f, "{}", self.as_display_str())
284    }
285}
286
287/// Error in parsing network from chain hash.
288#[derive(Debug, Clone, PartialEq, Eq)]
289#[non_exhaustive]
290pub struct UnknownChainHashError(ChainHash);
291
292impl Display for UnknownChainHashError {
293    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
294        write!(f, "unknown chain hash: {}", self.0)
295    }
296}
297
298#[cfg(feature = "std")]
299impl std::error::Error for UnknownChainHashError {
300    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
301}
302
303impl TryFrom<ChainHash> for Network {
304    type Error = UnknownChainHashError;
305
306    fn try_from(chain_hash: ChainHash) -> Result<Self, Self::Error> {
307        match chain_hash {
308            // Note: any new network entries must be matched against here.
309            ChainHash::BITCOIN => Ok(Network::Bitcoin),
310            ChainHash::TESTNET3 => Ok(Network::Testnet),
311            ChainHash::TESTNET4 => Ok(Network::Testnet4),
312            ChainHash::SIGNET => Ok(Network::Signet),
313            ChainHash::REGTEST => Ok(Network::Regtest),
314            _ => Err(UnknownChainHashError(chain_hash)),
315        }
316    }
317}
318
319#[cfg(test)]
320mod tests {
321    use super::Network;
322    use crate::consensus::encode::{deserialize, serialize};
323    use crate::p2p::ServiceFlags;
324
325    #[test]
326    fn serialize_test() {
327        assert_eq!(serialize(&Network::Bitcoin.magic()), &[0xf9, 0xbe, 0xb4, 0xd9]);
328        assert_eq!(
329            serialize(&Network::Testnet.magic()),
330            &[0x0b, 0x11, 0x09, 0x07]
331        );
332        assert_eq!(
333            serialize(&Network::Testnet4.magic()),
334            &[0x1c, 0x16, 0x3f, 0x28]
335        );
336        assert_eq!(serialize(&Network::Signet.magic()), &[0x0a, 0x03, 0xcf, 0x40]);
337        assert_eq!(serialize(&Network::Regtest.magic()), &[0xfa, 0xbf, 0xb5, 0xda]);
338
339        assert_eq!(deserialize(&[0xf9, 0xbe, 0xb4, 0xd9]).ok(), Some(Network::Bitcoin.magic()));
340        assert_eq!(
341            deserialize(&[0x0b, 0x11, 0x09, 0x07]).ok(),
342            Some(Network::Testnet.magic())
343        );
344        assert_eq!(
345            deserialize(&[0x1c, 0x16, 0x3f, 0x28]).ok(),
346            Some(Network::Testnet4.magic())
347        );
348        assert_eq!(deserialize(&[0x0a, 0x03, 0xcf, 0x40]).ok(), Some(Network::Signet.magic()));
349        assert_eq!(deserialize(&[0xfa, 0xbf, 0xb5, 0xda]).ok(), Some(Network::Regtest.magic()));
350    }
351
352    #[test]
353    fn string_test() {
354        assert_eq!(Network::Bitcoin.to_string(), "bitcoin");
355        assert_eq!(Network::Testnet.to_string(), "testnet");
356        assert_eq!(Network::Testnet4.to_string(), "testnet4");
357        assert_eq!(Network::Regtest.to_string(), "regtest");
358        assert_eq!(Network::Signet.to_string(), "signet");
359
360        assert_eq!("bitcoin".parse::<Network>().unwrap(), Network::Bitcoin);
361        assert_eq!("testnet".parse::<Network>().unwrap(), Network::Testnet);
362        assert_eq!("testnet4".parse::<Network>().unwrap(), Network::Testnet4);
363        assert_eq!("regtest".parse::<Network>().unwrap(), Network::Regtest);
364        assert_eq!("signet".parse::<Network>().unwrap(), Network::Signet);
365        assert!("fakenet".parse::<Network>().is_err());
366    }
367
368    #[test]
369    fn service_flags_test() {
370        let all = [
371            ServiceFlags::NETWORK,
372            ServiceFlags::GETUTXO,
373            ServiceFlags::BLOOM,
374            ServiceFlags::WITNESS,
375            ServiceFlags::COMPACT_FILTERS,
376            ServiceFlags::NETWORK_LIMITED,
377            ServiceFlags::P2P_V2,
378        ];
379
380        let mut flags = ServiceFlags::NONE;
381        for f in all.iter() {
382            assert!(!flags.has(*f));
383        }
384
385        flags |= ServiceFlags::WITNESS;
386        assert_eq!(flags, ServiceFlags::WITNESS);
387
388        let mut flags2 = flags | ServiceFlags::GETUTXO;
389        for f in all.iter() {
390            assert_eq!(flags2.has(*f), *f == ServiceFlags::WITNESS || *f == ServiceFlags::GETUTXO);
391        }
392
393        flags2 ^= ServiceFlags::WITNESS;
394        assert_eq!(flags2, ServiceFlags::GETUTXO);
395
396        flags2 |= ServiceFlags::COMPACT_FILTERS;
397        flags2 ^= ServiceFlags::GETUTXO;
398        assert_eq!(flags2, ServiceFlags::COMPACT_FILTERS);
399
400        // Test formatting.
401        assert_eq!("ServiceFlags(NONE)", ServiceFlags::NONE.to_string());
402        assert_eq!("ServiceFlags(WITNESS)", ServiceFlags::WITNESS.to_string());
403        let flag = ServiceFlags::WITNESS | ServiceFlags::BLOOM | ServiceFlags::NETWORK;
404        assert_eq!("ServiceFlags(NETWORK|BLOOM|WITNESS)", flag.to_string());
405        let flag = ServiceFlags::WITNESS | 0xf0.into();
406        assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string());
407    }
408
409    #[test]
410    #[cfg(feature = "serde")]
411    fn serde_roundtrip() {
412        use Network::*;
413        let tests = vec![
414            (Bitcoin, "bitcoin"),
415            (Testnet, "testnet"),
416            (Testnet4, "testnet4"),
417            (Signet, "signet"),
418            (Regtest, "regtest"),
419        ];
420
421        for tc in tests {
422            let network = tc.0;
423
424            let want = format!("\"{}\"", tc.1);
425            let got = serde_json::to_string(&tc.0).expect("failed to serialize network");
426            assert_eq!(got, want);
427
428            let back: Network = serde_json::from_str(&got).expect("failed to deserialize network");
429            assert_eq!(back, network);
430        }
431    }
432
433    #[test]
434    fn from_to_core_arg() {
435        let expected_pairs = [
436            (Network::Bitcoin, "main"),
437            (Network::Testnet, "test"),
438            (Network::Testnet4, "testnet4"),
439            (Network::Regtest, "regtest"),
440            (Network::Signet, "signet"),
441        ];
442
443        for (net, core_arg) in &expected_pairs {
444            assert_eq!(Network::from_core_arg(core_arg), Ok(*net));
445            assert_eq!(net.to_core_arg(), *core_arg);
446        }
447    }
448
449    #[cfg(feature = "serde")]
450    #[test]
451    fn serde_as_core_arg() {
452        #[derive(Serialize, Deserialize, PartialEq, Debug)]
453        #[serde(crate = "actual_serde")]
454        struct T {
455            #[serde(with = "crate::network::as_core_arg")]
456            pub network: Network,
457        }
458
459        serde_test::assert_tokens(
460            &T { network: Network::Bitcoin },
461            &[
462                serde_test::Token::Struct { name: "T", len: 1 },
463                serde_test::Token::Str("network"),
464                serde_test::Token::Str("main"),
465                serde_test::Token::StructEnd,
466            ],
467        );
468    }
469}