nakamoto_common/
network.rs

1//! Bitcoin peer network. Eg. *Mainnet*.
2use std::str::FromStr;
3
4use bitcoin::blockdata::block::{Block, BlockHeader};
5use bitcoin::consensus::params::Params;
6use bitcoin::hash_types::BlockHash;
7use bitcoin::hashes::hex::FromHex;
8use bitcoin::network::constants::ServiceFlags;
9
10use bitcoin_hashes::sha256d;
11
12use crate::block::Height;
13
14/// Peer services supported by nakamoto.
15#[derive(Debug, Copy, Clone)]
16pub enum Services {
17    /// Peers with compact filter support.
18    All,
19    /// Peers with only block support.
20    Chain,
21}
22
23impl From<Services> for ServiceFlags {
24    fn from(value: Services) -> Self {
25        match value {
26            Services::All => Self::COMPACT_FILTERS | Self::NETWORK,
27            Services::Chain => Self::NETWORK,
28        }
29    }
30}
31
32impl Default for Services {
33    fn default() -> Self {
34        Services::All
35    }
36}
37
38/// Bitcoin peer network.
39#[derive(Debug, Copy, Clone)]
40pub enum Network {
41    /// Bitcoin Mainnet.
42    Mainnet,
43    /// Bitcoin Testnet.
44    Testnet,
45    /// Bitcoin regression test net.
46    Regtest,
47    /// Bitcoin signet.
48    Signet,
49}
50
51impl Default for Network {
52    fn default() -> Self {
53        Self::Mainnet
54    }
55}
56
57impl FromStr for Network {
58    type Err = String;
59
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        match s {
62            "mainnet" | "bitcoin" => Ok(Self::Mainnet),
63            "testnet" => Ok(Self::Testnet),
64            "regtest" => Ok(Self::Regtest),
65            "signet" => Ok(Self::Signet),
66            _ => Err(format!("invalid network specified {:?}", s)),
67        }
68    }
69}
70
71impl From<Network> for bitcoin::Network {
72    fn from(value: Network) -> Self {
73        match value {
74            Network::Mainnet => Self::Bitcoin,
75            Network::Testnet => Self::Testnet,
76            Network::Regtest => Self::Regtest,
77            Network::Signet => Self::Signet,
78        }
79    }
80}
81
82impl From<bitcoin::Network> for Network {
83    fn from(value: bitcoin::Network) -> Self {
84        match value {
85            bitcoin::Network::Bitcoin => Self::Mainnet,
86            bitcoin::Network::Testnet => Self::Testnet,
87            bitcoin::Network::Signet => Self::Signet,
88            bitcoin::Network::Regtest => Self::Regtest,
89        }
90    }
91}
92
93impl Network {
94    /// Return the default listen port for the network.
95    pub fn port(&self) -> u16 {
96        match self {
97            Network::Mainnet => 8333,
98            Network::Testnet => 18333,
99            Network::Regtest => 18334,
100            Network::Signet => 38333,
101        }
102    }
103
104    /// Blockchain checkpoints.
105    pub fn checkpoints(&self) -> Box<dyn Iterator<Item = (Height, BlockHash)>> {
106        use crate::block::checkpoints;
107
108        let iter = match self {
109            Network::Mainnet => checkpoints::MAINNET,
110            Network::Testnet => checkpoints::TESTNET,
111            Network::Regtest => checkpoints::REGTEST,
112            Network::Signet => checkpoints::SIGNET,
113        }
114        .iter()
115        .cloned()
116        .map(|(height, hash)| {
117            let hash = BlockHash::from_hex(hash).unwrap();
118            (height, hash)
119        });
120
121        Box::new(iter)
122    }
123
124    /// Return the short string representation of this network.
125    pub fn as_str(&self) -> &'static str {
126        match self {
127            Network::Mainnet => "mainnet",
128            Network::Testnet => "testnet",
129            Network::Regtest => "regtest",
130            Network::Signet => "signet",
131        }
132    }
133
134    /// DNS seeds. Used to bootstrap the client's address book.
135    pub fn seeds(&self) -> &[&str] {
136        match self {
137            Network::Mainnet => &[
138                "seed.bitcoin.sipa.be",          // Pieter Wuille
139                "dnsseed.bluematt.me",           // Matt Corallo
140                "dnsseed.bitcoin.dashjr.org",    // Luke Dashjr
141                "seed.bitcoinstats.com",         // Christian Decker
142                "seed.bitcoin.jonasschnelli.ch", // Jonas Schnelli
143                "seed.btc.petertodd.org",        // Peter Todd
144                "seed.bitcoin.sprovoost.nl",     // Sjors Provoost
145                "dnsseed.emzy.de",               // Stephan Oeste
146                "seed.bitcoin.wiz.biz",          // Jason Maurice
147                "seed.cloudhead.io",             // Alexis Sellier
148            ],
149            Network::Testnet => &[
150                "testnet-seed.bitcoin.jonasschnelli.ch",
151                "seed.tbtc.petertodd.org",
152                "seed.testnet.bitcoin.sprovoost.nl",
153                "testnet-seed.bluematt.me",
154            ],
155            Network::Regtest => &[], // No seeds
156            Network::Signet => &["seed.signet.bitcoin.sprovoost.nl"],
157        }
158    }
159}
160
161impl Network {
162    /// Get the genesis block header.
163    ///
164    /// ```
165    /// use nakamoto_common::network::Network;
166    ///
167    /// let network = Network::Mainnet;
168    /// let genesis = network.genesis();
169    ///
170    /// assert_eq!(network.genesis_hash(), genesis.block_hash());
171    /// ```
172    pub fn genesis(&self) -> BlockHeader {
173        self.genesis_block().header
174    }
175
176    /// Get the genesis block.
177    pub fn genesis_block(&self) -> Block {
178        use bitcoin::blockdata::constants;
179
180        constants::genesis_block((*self).into())
181    }
182
183    /// Get the hash of the genesis block of this network.
184    pub fn genesis_hash(&self) -> BlockHash {
185        use crate::block::genesis;
186        use bitcoin_hashes::Hash;
187
188        let hash = match self {
189            Self::Mainnet => genesis::MAINNET,
190            Self::Testnet => genesis::TESTNET,
191            Self::Regtest => genesis::REGTEST,
192            Self::Signet => genesis::SIGNET,
193        };
194        BlockHash::from_hash(
195            sha256d::Hash::from_slice(hash)
196                .expect("the genesis hash has the right number of bytes"),
197        )
198    }
199
200    /// Get the consensus parameters for this network.
201    pub fn params(&self) -> Params {
202        Params::new((*self).into())
203    }
204
205    /// Get the network magic number for this network.
206    pub fn magic(&self) -> u32 {
207        bitcoin::Network::from(*self).magic()
208    }
209}