lexe_common/ln/
network.rs1use std::{fmt, fmt::Display, str::FromStr};
2
3use anyhow::anyhow;
4use bitcoin::{
5 blockdata::constants::{self, ChainHash},
6 hash_types::BlockHash,
7 hashes::Hash as _,
8};
9use lightning_invoice::Currency;
10#[cfg(any(test, feature = "test-utils"))]
11use proptest_derive::Arbitrary;
12use serde::Serialize;
13use serde_with::DeserializeFromStr;
14use strum::VariantArray;
15
16#[derive(Copy, Clone, Debug, Eq, PartialEq, DeserializeFromStr, VariantArray)]
23#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
24pub enum Network {
25 Mainnet,
26 Testnet3,
27 Testnet4,
28 Regtest,
29 Signet,
30}
31
32impl Network {
33 #[inline]
36 pub fn to_bitcoin(self) -> bitcoin::Network {
37 bitcoin::Network::from(self)
38 }
39
40 pub fn as_str(self) -> &'static str {
41 match self {
42 Self::Mainnet => "mainnet",
43 Self::Testnet3 => "testnet3",
44 Self::Testnet4 => "testnet4",
45 Self::Regtest => "regtest",
46 Self::Signet => "signet",
47 }
48 }
49
50 pub fn genesis_block_hash(self) -> BlockHash {
52 let chain_hash = Self::genesis_chain_hash(self);
53 BlockHash::from_byte_array(chain_hash.to_bytes())
54 }
55
56 #[inline]
59 pub fn genesis_chain_hash(self) -> ChainHash {
60 constants::ChainHash::using_genesis_block(self.to_bitcoin())
61 }
62}
63
64impl FromStr for Network {
65 type Err = anyhow::Error;
66 fn from_str(s: &str) -> Result<Self, Self::Err> {
67 match s {
68 "mainnet" => Ok(Self::Mainnet),
69 "testnet3" => Ok(Self::Testnet3),
70 "testnet4" => Ok(Self::Testnet4),
71 "regtest" => Ok(Self::Regtest),
72 "signet" => Ok(Self::Signet),
73 _ => Err(anyhow!("Invalid `Network`")),
74 }
75 }
76}
77
78impl Display for Network {
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 write!(f, "{}", self.as_str())
81 }
82}
83
84impl From<bitcoin::Network> for Network {
85 fn from(network: bitcoin::Network) -> Self {
86 match network {
87 bitcoin::Network::Bitcoin => Self::Mainnet,
88 bitcoin::Network::Testnet => Self::Testnet3,
89 bitcoin::Network::Testnet4 => Self::Testnet4,
90 bitcoin::Network::Signet => Self::Signet,
91 bitcoin::Network::Regtest => Self::Regtest,
92 }
93 }
94}
95
96impl From<Network> for bitcoin::Network {
97 fn from(lx: Network) -> Self {
98 match lx {
99 Network::Mainnet => Self::Bitcoin,
100 Network::Testnet3 => Self::Testnet,
101 Network::Testnet4 => Self::Testnet4,
102 Network::Regtest => Self::Regtest,
103 Network::Signet => Self::Signet,
104 }
105 }
106}
107
108impl From<Network> for Currency {
109 fn from(lx: Network) -> Self {
110 match lx {
111 Network::Mainnet => Self::Bitcoin,
112 Network::Testnet3 | Network::Testnet4 => Self::BitcoinTestnet,
113 Network::Regtest => Self::Regtest,
114 Network::Signet => Self::Signet,
115 }
116 }
117}
118
119impl Serialize for Network {
120 fn serialize<S: serde::Serializer>(
121 &self,
122 serializer: S,
123 ) -> Result<S::Ok, S::Error> {
124 self.as_str().serialize(serializer)
125 }
126}
127
128#[cfg(test)]
129mod test {
130 use lexe_hex::hex;
131
132 use super::*;
133 use crate::test_utils::roundtrip;
134
135 #[test]
136 fn network_roundtrip() {
137 let expected_ser =
138 r#"["mainnet","testnet3","testnet4","regtest","signet"]"#;
139 roundtrip::json_unit_enum_backwards_compat::<Network>(expected_ser);
140 roundtrip::fromstr_display_roundtrip_proptest::<Network>();
141 }
142
143 #[test]
145 fn check_precomputed_genesis_block_hashes() {
146 for network in Network::VARIANTS {
147 let precomputed = network.genesis_block_hash();
148 let computed = constants::genesis_block(network.to_bitcoin())
149 .header
150 .block_hash();
151 assert_eq!(precomputed, computed);
152 }
153 }
154
155 #[test]
157 fn absolutely_check_mainnet_genesis_hash() {
158 let expected = hex::decode(
159 "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000",
160 )
161 .unwrap();
162 let actual = Network::Mainnet.genesis_chain_hash();
163 assert_eq!(actual.as_bytes().as_slice(), expected.as_slice());
164 }
165}