use core::fmt;
use core::fmt::Display;
use core::str::FromStr;
use internals::write_err;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::consensus::Params;
use crate::constants::ChainHash;
use crate::p2p::Magic;
use crate::prelude::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NetworkKind {
Main,
Test,
}
impl NetworkKind {
pub fn is_mainnet(&self) -> bool { *self == NetworkKind::Main }
}
impl From<Network> for NetworkKind {
fn from(n: Network) -> Self {
use Network::*;
match n {
Bitcoin => NetworkKind::Main,
Testnet | Testnet4 | Signet | Regtest => NetworkKind::Test,
}
}
}
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[non_exhaustive]
pub enum Network {
Bitcoin,
Testnet,
Testnet4,
Signet,
Regtest,
}
impl Network {
pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
pub fn magic(self) -> Magic { Magic::from(self) }
pub fn to_core_arg(self) -> &'static str {
match self {
Network::Bitcoin => "main",
Network::Testnet => "test",
Network::Testnet4 => "testnet4",
Network::Signet => "signet",
Network::Regtest => "regtest",
}
}
pub fn from_core_arg(core_arg: &str) -> Result<Self, ParseNetworkError> {
use Network::*;
let network = match core_arg {
"main" => Bitcoin,
"test" => Testnet,
"testnet4" => Testnet4,
"signet" => Signet,
"regtest" => Regtest,
_ => return Err(ParseNetworkError(core_arg.to_owned())),
};
Ok(network)
}
pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block_const(self) }
pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> {
Network::try_from(chain_hash).ok()
}
pub const fn params(self) -> &'static Params {
match self {
Network::Bitcoin => &Params::BITCOIN,
Network::Testnet => &Params::TESTNET3,
Network::Testnet4 => &Params::TESTNET4,
Network::Signet => &Params::SIGNET,
Network::Regtest => &Params::REGTEST,
}
}
const fn as_display_str(self) -> &'static str {
match self {
Network::Bitcoin => "bitcoin",
Network::Testnet => "testnet",
Network::Testnet4 => "testnet4",
Network::Signet => "signet",
Network::Regtest => "regtest",
}
}
}
#[cfg(feature = "serde")]
pub mod as_core_arg {
#![allow(missing_docs)]
use crate::Network;
pub fn serialize<S>(network: &Network, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(network.to_core_arg())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Network, D::Error>
where
D: serde::Deserializer<'de>,
{
struct NetworkVisitor;
impl<'de> serde::de::Visitor<'de> for NetworkVisitor {
type Value = Network;
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
Network::from_core_arg(s).map_err(|_| {
E::invalid_value(
serde::de::Unexpected::Str(s),
&"bitcoin network encoded as a string (either main, test, testnet4, signet or regtest)",
)
})
}
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
formatter,
"bitcoin network encoded as a string (either main, test, testnet4, signet or regtest)"
)
}
}
deserializer.deserialize_str(NetworkVisitor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ParseNetworkError(String);
impl fmt::Display for ParseNetworkError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write_err!(f, "failed to parse {} as network", self.0; self)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseNetworkError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
impl FromStr for Network {
type Err = ParseNetworkError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bitcoin" => Ok(Network::Bitcoin),
"testnet" => Ok(Network::Testnet),
"testnet4" => Ok(Network::Testnet4),
"signet" => Ok(Network::Signet),
"regtest" => Ok(Network::Regtest),
_ => Err(ParseNetworkError(s.to_owned())),
}
}
}
impl fmt::Display for Network {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.as_display_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct UnknownChainHashError(ChainHash);
impl Display for UnknownChainHashError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unknown chain hash: {}", self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for UnknownChainHashError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
impl TryFrom<ChainHash> for Network {
type Error = UnknownChainHashError;
fn try_from(chain_hash: ChainHash) -> Result<Self, Self::Error> {
match chain_hash {
ChainHash::BITCOIN => Ok(Network::Bitcoin),
ChainHash::TESTNET3 => Ok(Network::Testnet),
ChainHash::TESTNET4 => Ok(Network::Testnet4),
ChainHash::SIGNET => Ok(Network::Signet),
ChainHash::REGTEST => Ok(Network::Regtest),
_ => Err(UnknownChainHashError(chain_hash)),
}
}
}
#[cfg(test)]
mod tests {
use super::Network;
use crate::consensus::encode::{deserialize, serialize};
use crate::p2p::ServiceFlags;
#[test]
fn serialize_test() {
assert_eq!(serialize(&Network::Bitcoin.magic()), &[0xf9, 0xbe, 0xb4, 0xd9]);
assert_eq!(
serialize(&Network::Testnet.magic()),
&[0x0b, 0x11, 0x09, 0x07]
);
assert_eq!(
serialize(&Network::Testnet4.magic()),
&[0x1c, 0x16, 0x3f, 0x28]
);
assert_eq!(serialize(&Network::Signet.magic()), &[0x0a, 0x03, 0xcf, 0x40]);
assert_eq!(serialize(&Network::Regtest.magic()), &[0xfa, 0xbf, 0xb5, 0xda]);
assert_eq!(deserialize(&[0xf9, 0xbe, 0xb4, 0xd9]).ok(), Some(Network::Bitcoin.magic()));
assert_eq!(
deserialize(&[0x0b, 0x11, 0x09, 0x07]).ok(),
Some(Network::Testnet.magic())
);
assert_eq!(
deserialize(&[0x1c, 0x16, 0x3f, 0x28]).ok(),
Some(Network::Testnet4.magic())
);
assert_eq!(deserialize(&[0x0a, 0x03, 0xcf, 0x40]).ok(), Some(Network::Signet.magic()));
assert_eq!(deserialize(&[0xfa, 0xbf, 0xb5, 0xda]).ok(), Some(Network::Regtest.magic()));
}
#[test]
fn string_test() {
assert_eq!(Network::Bitcoin.to_string(), "bitcoin");
assert_eq!(Network::Testnet.to_string(), "testnet");
assert_eq!(Network::Testnet4.to_string(), "testnet4");
assert_eq!(Network::Regtest.to_string(), "regtest");
assert_eq!(Network::Signet.to_string(), "signet");
assert_eq!("bitcoin".parse::<Network>().unwrap(), Network::Bitcoin);
assert_eq!("testnet".parse::<Network>().unwrap(), Network::Testnet);
assert_eq!("testnet4".parse::<Network>().unwrap(), Network::Testnet4);
assert_eq!("regtest".parse::<Network>().unwrap(), Network::Regtest);
assert_eq!("signet".parse::<Network>().unwrap(), Network::Signet);
assert!("fakenet".parse::<Network>().is_err());
}
#[test]
fn service_flags_test() {
let all = [
ServiceFlags::NETWORK,
ServiceFlags::GETUTXO,
ServiceFlags::BLOOM,
ServiceFlags::WITNESS,
ServiceFlags::COMPACT_FILTERS,
ServiceFlags::NETWORK_LIMITED,
ServiceFlags::P2P_V2,
];
let mut flags = ServiceFlags::NONE;
for f in all.iter() {
assert!(!flags.has(*f));
}
flags |= ServiceFlags::WITNESS;
assert_eq!(flags, ServiceFlags::WITNESS);
let mut flags2 = flags | ServiceFlags::GETUTXO;
for f in all.iter() {
assert_eq!(flags2.has(*f), *f == ServiceFlags::WITNESS || *f == ServiceFlags::GETUTXO);
}
flags2 ^= ServiceFlags::WITNESS;
assert_eq!(flags2, ServiceFlags::GETUTXO);
flags2 |= ServiceFlags::COMPACT_FILTERS;
flags2 ^= ServiceFlags::GETUTXO;
assert_eq!(flags2, ServiceFlags::COMPACT_FILTERS);
assert_eq!("ServiceFlags(NONE)", ServiceFlags::NONE.to_string());
assert_eq!("ServiceFlags(WITNESS)", ServiceFlags::WITNESS.to_string());
let flag = ServiceFlags::WITNESS | ServiceFlags::BLOOM | ServiceFlags::NETWORK;
assert_eq!("ServiceFlags(NETWORK|BLOOM|WITNESS)", flag.to_string());
let flag = ServiceFlags::WITNESS | 0xf0.into();
assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string());
}
#[test]
#[cfg(feature = "serde")]
fn serde_roundtrip() {
use Network::*;
let tests = vec![
(Bitcoin, "bitcoin"),
(Testnet, "testnet"),
(Testnet4, "testnet4"),
(Signet, "signet"),
(Regtest, "regtest"),
];
for tc in tests {
let network = tc.0;
let want = format!("\"{}\"", tc.1);
let got = serde_json::to_string(&tc.0).expect("failed to serialize network");
assert_eq!(got, want);
let back: Network = serde_json::from_str(&got).expect("failed to deserialize network");
assert_eq!(back, network);
}
}
#[test]
fn from_to_core_arg() {
let expected_pairs = [
(Network::Bitcoin, "main"),
(Network::Testnet, "test"),
(Network::Testnet4, "testnet4"),
(Network::Regtest, "regtest"),
(Network::Signet, "signet"),
];
for (net, core_arg) in &expected_pairs {
assert_eq!(Network::from_core_arg(core_arg), Ok(*net));
assert_eq!(net.to_core_arg(), *core_arg);
}
}
#[cfg(feature = "serde")]
#[test]
fn serde_as_core_arg() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(crate = "actual_serde")]
struct T {
#[serde(with = "crate::network::as_core_arg")]
pub network: Network,
}
serde_test::assert_tokens(
&T { network: Network::Bitcoin },
&[
serde_test::Token::Struct { name: "T", len: 1 },
serde_test::Token::Str("network"),
serde_test::Token::Str("main"),
serde_test::Token::StructEnd,
],
);
}
}