1use 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#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub enum NetworkKind {
37 Main,
39 Test,
41}
42
43impl NetworkKind {
46 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#[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 Bitcoin,
70 Testnet,
73 Testnet4,
76 Signet,
78 Regtest,
80}
81
82impl Network {
83 pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
95
96 pub fn magic(self) -> Magic { Magic::from(self) }
109
110 pub fn to_core_arg(self) -> &'static str {
120 match self {
121 Network::Bitcoin => "main",
122 Network::Testnet => "test",
124 Network::Testnet4 => "testnet4",
125 Network::Signet => "signet",
126 Network::Regtest => "regtest",
127 }
128 }
129
130 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 pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block_const(self) }
165
166 pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> {
177 Network::try_from(chain_hash).ok()
178 }
179
180 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 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 #![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#[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 "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#[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 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 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}