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)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
74#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
75pub enum Network {
76 Bitcoin,
78 Testnet,
81 Testnet4,
84 Signet,
86 Regtest,
88}
89
90impl Network {
91 pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
103
104 pub fn magic(self) -> Magic { Magic::from(self) }
117
118 pub fn to_core_arg(self) -> &'static str {
128 match self {
129 Network::Bitcoin => "main",
130 Network::Testnet => "test",
132 Network::Testnet4 => "testnet4",
133 Network::Signet => "signet",
134 Network::Regtest => "regtest",
135 }
136 }
137
138 pub fn from_core_arg(core_arg: &str) -> Result<Self, ParseNetworkError> {
148 use Network::*;
149
150 let network = match core_arg {
151 "main" => Bitcoin,
152 "test" => Testnet,
153 "testnet4" => Testnet4,
154 "signet" => Signet,
155 "regtest" => Regtest,
156 _ => return Err(ParseNetworkError(core_arg.to_owned())),
157 };
158 Ok(network)
159 }
160
161 pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block_const(self) }
173
174 pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> {
185 Network::try_from(chain_hash).ok()
186 }
187
188 pub const fn params(self) -> &'static Params {
190 match self {
191 Network::Bitcoin => &Params::BITCOIN,
192 Network::Testnet => &Params::TESTNET3,
193 Network::Testnet4 => &Params::TESTNET4,
194 Network::Signet => &Params::SIGNET,
195 Network::Regtest => &Params::REGTEST,
196 }
197 }
198
199 const fn as_display_str(self) -> &'static str {
202 match self {
203 Network::Bitcoin => "bitcoin",
204 Network::Testnet => "testnet",
205 Network::Testnet4 => "testnet4",
206 Network::Signet => "signet",
207 Network::Regtest => "regtest",
208 }
209 }
210}
211
212#[cfg(feature = "serde")]
213pub mod as_core_arg {
214 #![allow(missing_docs)]
216
217 use crate::Network;
218
219 pub fn serialize<S>(network: &Network, serializer: S) -> Result<S::Ok, S::Error>
220 where
221 S: serde::Serializer,
222 {
223 serializer.serialize_str(network.to_core_arg())
224 }
225
226 pub fn deserialize<'de, D>(deserializer: D) -> Result<Network, D::Error>
227 where
228 D: serde::Deserializer<'de>,
229 {
230 struct NetworkVisitor;
231
232 impl<'de> serde::de::Visitor<'de> for NetworkVisitor {
233 type Value = Network;
234
235 fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
236 Network::from_core_arg(s).map_err(|_| {
237 E::invalid_value(
238 serde::de::Unexpected::Str(s),
239 &"bitcoin network encoded as a string (either main, test, testnet4, signet or regtest)",
240 )
241 })
242 }
243
244 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
245 write!(
246 formatter,
247 "bitcoin network encoded as a string (either main, test, testnet4, signet or regtest)"
248 )
249 }
250 }
251
252 deserializer.deserialize_str(NetworkVisitor)
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258#[non_exhaustive]
259pub struct ParseNetworkError(String);
260
261impl fmt::Display for ParseNetworkError {
262 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
263 write_err!(f, "failed to parse {} as network", self.0; self)
264 }
265}
266
267#[cfg(feature = "std")]
268impl std::error::Error for ParseNetworkError {
269 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
270}
271
272impl FromStr for Network {
273 type Err = ParseNetworkError;
274
275 #[inline]
276 fn from_str(s: &str) -> Result<Self, Self::Err> {
277 match s {
278 "bitcoin" => Ok(Network::Bitcoin),
279 "testnet" => Ok(Network::Testnet),
281 "testnet4" => Ok(Network::Testnet4),
282 "signet" => Ok(Network::Signet),
283 "regtest" => Ok(Network::Regtest),
284 _ => Err(ParseNetworkError(s.to_owned())),
285 }
286 }
287}
288
289impl fmt::Display for Network {
290 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
291 write!(f, "{}", self.as_display_str())
292 }
293}
294
295#[derive(Debug, Clone, PartialEq, Eq)]
297#[non_exhaustive]
298pub struct UnknownChainHashError(ChainHash);
299
300impl Display for UnknownChainHashError {
301 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302 write!(f, "unknown chain hash: {}", self.0)
303 }
304}
305
306#[cfg(feature = "std")]
307impl std::error::Error for UnknownChainHashError {
308 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
309}
310
311impl TryFrom<ChainHash> for Network {
312 type Error = UnknownChainHashError;
313
314 fn try_from(chain_hash: ChainHash) -> Result<Self, Self::Error> {
315 match chain_hash {
316 ChainHash::BITCOIN => Ok(Network::Bitcoin),
318 ChainHash::TESTNET3 => Ok(Network::Testnet),
319 ChainHash::TESTNET4 => Ok(Network::Testnet4),
320 ChainHash::SIGNET => Ok(Network::Signet),
321 ChainHash::REGTEST => Ok(Network::Regtest),
322 _ => Err(UnknownChainHashError(chain_hash)),
323 }
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::Network;
330 use crate::consensus::encode::{deserialize, serialize};
331 use crate::p2p::ServiceFlags;
332
333 #[test]
334 fn serialize_test() {
335 assert_eq!(serialize(&Network::Bitcoin.magic()), &[0xf9, 0xbe, 0xb4, 0xd9]);
336 assert_eq!(
337 serialize(&Network::Testnet.magic()),
338 &[0x0b, 0x11, 0x09, 0x07]
339 );
340 assert_eq!(
341 serialize(&Network::Testnet4.magic()),
342 &[0x1c, 0x16, 0x3f, 0x28]
343 );
344 assert_eq!(serialize(&Network::Signet.magic()), &[0x0a, 0x03, 0xcf, 0x40]);
345 assert_eq!(serialize(&Network::Regtest.magic()), &[0xfa, 0xbf, 0xb5, 0xda]);
346
347 assert_eq!(deserialize(&[0xf9, 0xbe, 0xb4, 0xd9]).ok(), Some(Network::Bitcoin.magic()));
348 assert_eq!(
349 deserialize(&[0x0b, 0x11, 0x09, 0x07]).ok(),
350 Some(Network::Testnet.magic())
351 );
352 assert_eq!(
353 deserialize(&[0x1c, 0x16, 0x3f, 0x28]).ok(),
354 Some(Network::Testnet4.magic())
355 );
356 assert_eq!(deserialize(&[0x0a, 0x03, 0xcf, 0x40]).ok(), Some(Network::Signet.magic()));
357 assert_eq!(deserialize(&[0xfa, 0xbf, 0xb5, 0xda]).ok(), Some(Network::Regtest.magic()));
358 }
359
360 #[test]
361 fn string_test() {
362 assert_eq!(Network::Bitcoin.to_string(), "bitcoin");
363 assert_eq!(Network::Testnet.to_string(), "testnet");
364 assert_eq!(Network::Testnet4.to_string(), "testnet4");
365 assert_eq!(Network::Regtest.to_string(), "regtest");
366 assert_eq!(Network::Signet.to_string(), "signet");
367
368 assert_eq!("bitcoin".parse::<Network>().unwrap(), Network::Bitcoin);
369 assert_eq!("testnet".parse::<Network>().unwrap(), Network::Testnet);
370 assert_eq!("testnet4".parse::<Network>().unwrap(), Network::Testnet4);
371 assert_eq!("regtest".parse::<Network>().unwrap(), Network::Regtest);
372 assert_eq!("signet".parse::<Network>().unwrap(), Network::Signet);
373 assert!("fakenet".parse::<Network>().is_err());
374 }
375
376 #[test]
377 fn service_flags_test() {
378 let all = [
379 ServiceFlags::NETWORK,
380 ServiceFlags::GETUTXO,
381 ServiceFlags::BLOOM,
382 ServiceFlags::WITNESS,
383 ServiceFlags::COMPACT_FILTERS,
384 ServiceFlags::NETWORK_LIMITED,
385 ServiceFlags::P2P_V2,
386 ];
387
388 let mut flags = ServiceFlags::NONE;
389 for f in all.iter() {
390 assert!(!flags.has(*f));
391 }
392
393 flags |= ServiceFlags::WITNESS;
394 assert_eq!(flags, ServiceFlags::WITNESS);
395
396 let mut flags2 = flags | ServiceFlags::GETUTXO;
397 for f in all.iter() {
398 assert_eq!(flags2.has(*f), *f == ServiceFlags::WITNESS || *f == ServiceFlags::GETUTXO);
399 }
400
401 flags2 ^= ServiceFlags::WITNESS;
402 assert_eq!(flags2, ServiceFlags::GETUTXO);
403
404 flags2 |= ServiceFlags::COMPACT_FILTERS;
405 flags2 ^= ServiceFlags::GETUTXO;
406 assert_eq!(flags2, ServiceFlags::COMPACT_FILTERS);
407
408 assert_eq!("ServiceFlags(NONE)", ServiceFlags::NONE.to_string());
410 assert_eq!("ServiceFlags(WITNESS)", ServiceFlags::WITNESS.to_string());
411 let flag = ServiceFlags::WITNESS | ServiceFlags::BLOOM | ServiceFlags::NETWORK;
412 assert_eq!("ServiceFlags(NETWORK|BLOOM|WITNESS)", flag.to_string());
413 let flag = ServiceFlags::WITNESS | 0xf0.into();
414 assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string());
415 }
416
417 #[test]
418 #[cfg(feature = "serde")]
419 fn serde_roundtrip() {
420 use Network::*;
421 let tests = vec![
422 (Bitcoin, "bitcoin"),
423 (Testnet, "testnet"),
424 (Testnet4, "testnet4"),
425 (Signet, "signet"),
426 (Regtest, "regtest"),
427 ];
428
429 for tc in tests {
430 let network = tc.0;
431
432 let want = format!("\"{}\"", tc.1);
433 let got = serde_json::to_string(&tc.0).expect("failed to serialize network");
434 assert_eq!(got, want);
435
436 let back: Network = serde_json::from_str(&got).expect("failed to deserialize network");
437 assert_eq!(back, network);
438 }
439 }
440
441 #[test]
442 fn from_to_core_arg() {
443 let expected_pairs = [
444 (Network::Bitcoin, "main"),
445 (Network::Testnet, "test"),
446 (Network::Testnet4, "testnet4"),
447 (Network::Regtest, "regtest"),
448 (Network::Signet, "signet"),
449 ];
450
451 for (net, core_arg) in &expected_pairs {
452 assert_eq!(Network::from_core_arg(core_arg), Ok(*net));
453 assert_eq!(net.to_core_arg(), *core_arg);
454 }
455 }
456
457 #[cfg(feature = "serde")]
458 #[test]
459 fn serde_as_core_arg() {
460 #[derive(Serialize, Deserialize, PartialEq, Debug)]
461 #[serde(crate = "actual_serde")]
462 struct T {
463 #[serde(with = "crate::network::as_core_arg")]
464 pub network: Network,
465 }
466
467 serde_test::assert_tokens(
468 &T { network: Network::Bitcoin },
469 &[
470 serde_test::Token::Struct { name: "T", len: 1 },
471 serde_test::Token::Str("network"),
472 serde_test::Token::Str("main"),
473 serde_test::Token::StructEnd,
474 ],
475 );
476 }
477}