groestlcoin/
network.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Groestlcoin network.
4//!
5//! The term "network" is overloaded, here [`Network`] refers to the specific
6//! Groestlcoin network we are operating on e.g., signet, regtest. The terms
7//! "network" and "chain" are often used interchangeably for this concept.
8//!
9//! # Example: encoding a network's magic bytes
10//!
11//! ```rust
12//! use groestlcoin::Network;
13//! use groestlcoin::consensus::encode::serialize;
14//!
15//! let network = Network::Groestlcoin;
16//! let bytes = serialize(&network.magic());
17//!
18//! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD4]);
19//! ```
20
21use core::convert::TryFrom;
22use core::fmt;
23use core::fmt::Display;
24use core::str::FromStr;
25
26use internals::write_err;
27#[cfg(feature = "serde")]
28use serde::{Deserialize, Serialize};
29
30use crate::constants::ChainHash;
31use crate::p2p::Magic;
32use crate::prelude::{String, ToOwned};
33
34/// The cryptocurrency network to act on.
35#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
38#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
39#[non_exhaustive]
40pub enum Network {
41    /// Mainnet Groestlcoin.
42    Groestlcoin,
43    /// Groestlcoin's testnet network.
44    Testnet,
45    /// Groestlcoin's signet network.
46    Signet,
47    /// Groestlcoin's regtest network.
48    Regtest,
49}
50
51impl Network {
52    /// Creates a `Network` from the magic bytes.
53    ///
54    /// # Examples
55    ///
56    /// ```rust
57    /// use groestlcoin::p2p::Magic;
58    /// use groestlcoin::Network;
59    /// use std::convert::TryFrom;
60    ///
61    /// assert_eq!(Ok(Network::Groestlcoin), Network::try_from(Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD4])));
62    /// assert_eq!(None, Network::from_magic(Magic::from_bytes([0xFF, 0xFF, 0xFF, 0xFF])));
63    /// ```
64    pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
65
66    /// Return the network magic bytes, which should be encoded little-endian
67    /// at the start of every message
68    ///
69    /// # Examples
70    ///
71    /// ```rust
72    /// use groestlcoin::p2p::Magic;
73    /// use groestlcoin::Network;
74    ///
75    /// let network = Network::Groestlcoin;
76    /// assert_eq!(network.magic(), Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD4]));
77    /// ```
78    pub fn magic(self) -> Magic { Magic::from(self) }
79
80    /// Converts a `Network` to its equivalent `groestlcoind -chain` argument name.
81    ///
82    /// ```bash
83    /// $ groestlcoin-23.0/bin/groestlcoind --help | grep -C 3 '\-chain=<chain>'
84    /// Chain selection options:
85    ///
86    /// -chain=<chain>
87    /// Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest
88    /// ```
89    pub fn to_core_arg(self) -> &'static str {
90        match self {
91            Network::Groestlcoin => "main",
92            Network::Testnet => "test",
93            Network::Signet => "signet",
94            Network::Regtest => "regtest",
95        }
96    }
97
98    /// Converts a `groestlcoind -chain` argument name to its equivalent `Network`.
99    ///
100    /// ```bash
101    /// $ groestlcoin-23.0/bin/groestlcoind --help | grep -C 3 '\-chain=<chain>'
102    /// Chain selection options:
103    ///
104    /// -chain=<chain>
105    /// Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest
106    /// ```
107    pub fn from_core_arg(core_arg: &str) -> Result<Self, ParseNetworkError> {
108        use Network::*;
109
110        let network = match core_arg {
111            "main" => Groestlcoin,
112            "test" => Testnet,
113            "signet" => Signet,
114            "regtest" => Regtest,
115            _ => return Err(ParseNetworkError(core_arg.to_owned())),
116        };
117        Ok(network)
118    }
119
120    /// Return the network's chain hash (genesis block hash).
121    ///
122    /// # Examples
123    ///
124    /// ```rust
125    /// use groestlcoin::Network;
126    /// use groestlcoin::blockdata::constants::ChainHash;
127    ///
128    /// let network = Network::Groestlcoin;
129    /// assert_eq!(network.chain_hash(), ChainHash::GROESTLCOIN);
130    /// ```
131    pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block(self) }
132
133    /// Creates a `Network` from the chain hash (genesis block hash).
134    ///
135    /// # Examples
136    ///
137    /// ```rust
138    /// use groestlcoin::Network;
139    /// use groestlcoin::blockdata::constants::ChainHash;
140    /// use std::convert::TryFrom;
141    ///
142    /// assert_eq!(Ok(Network::Groestlcoin), Network::try_from(ChainHash::GROESTLCOIN));
143    /// ```
144    pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> {
145        Network::try_from(chain_hash).ok()
146    }
147}
148
149#[cfg(feature = "serde")]
150pub mod as_core_arg {
151    //! Module for serialization/deserialization of network variants into/from Groestlcoin Core values
152    #![allow(missing_docs)]
153
154    use serde;
155
156    use crate::Network;
157
158    pub fn serialize<S>(network: &Network, serializer: S) -> Result<S::Ok, S::Error>
159    where
160        S: serde::Serializer,
161    {
162        serializer.serialize_str(network.to_core_arg())
163    }
164
165    pub fn deserialize<'de, D>(deserializer: D) -> Result<Network, D::Error>
166    where
167        D: serde::Deserializer<'de>,
168    {
169        struct NetworkVisitor;
170
171        impl<'de> serde::de::Visitor<'de> for NetworkVisitor {
172            type Value = Network;
173
174            fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
175                Network::from_core_arg(s).map_err(|_| {
176                    E::invalid_value(
177                        serde::de::Unexpected::Str(s),
178                        &"groestlcoin network encoded as a string (either main, test, signet or regtest)",
179                    )
180                })
181            }
182
183            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
184                write!(
185                    formatter,
186                    "groestlcoin network encoded as a string (either main, test, signet or regtest)"
187                )
188            }
189        }
190
191        deserializer.deserialize_str(NetworkVisitor)
192    }
193}
194
195/// An error in parsing network string.
196#[derive(Debug, Clone, PartialEq, Eq)]
197#[non_exhaustive]
198pub struct ParseNetworkError(String);
199
200impl fmt::Display for ParseNetworkError {
201    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
202        write_err!(f, "failed to parse {} as network", self.0; self)
203    }
204}
205
206#[cfg(feature = "std")]
207impl std::error::Error for ParseNetworkError {
208    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
209}
210
211impl FromStr for Network {
212    type Err = ParseNetworkError;
213
214    #[inline]
215    fn from_str(s: &str) -> Result<Self, Self::Err> {
216        use Network::*;
217
218        let network = match s {
219            "groestlcoin" => Groestlcoin,
220            "testnet" => Testnet,
221            "signet" => Signet,
222            "regtest" => Regtest,
223            _ => return Err(ParseNetworkError(s.to_owned())),
224        };
225        Ok(network)
226    }
227}
228
229impl fmt::Display for Network {
230    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
231        use Network::*;
232
233        let s = match *self {
234            Groestlcoin => "groestlcoin",
235            Testnet => "testnet",
236            Signet => "signet",
237            Regtest => "regtest",
238        };
239        write!(f, "{}", s)
240    }
241}
242
243/// Error in parsing network from chain hash.
244#[derive(Debug, Clone, PartialEq, Eq)]
245#[non_exhaustive]
246pub struct UnknownChainHashError(ChainHash);
247
248impl Display for UnknownChainHashError {
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250        write!(f, "unknown chain hash: {}", self.0)
251    }
252}
253
254#[cfg(feature = "std")]
255impl std::error::Error for UnknownChainHashError {
256    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
257}
258
259impl TryFrom<ChainHash> for Network {
260    type Error = UnknownChainHashError;
261
262    fn try_from(chain_hash: ChainHash) -> Result<Self, Self::Error> {
263        match chain_hash {
264            // Note: any new network entries must be matched against here.
265            ChainHash::GROESTLCOIN => Ok(Network::Groestlcoin),
266            ChainHash::TESTNET => Ok(Network::Testnet),
267            ChainHash::SIGNET => Ok(Network::Signet),
268            //ChainHash::REGTEST => Ok(Network::Regtest),
269            _ => Err(UnknownChainHashError(chain_hash)),
270        }
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::Network;
277    use crate::consensus::encode::{deserialize, serialize};
278    use crate::p2p::ServiceFlags;
279
280    #[test]
281    fn serialize_test() {
282        assert_eq!(serialize(&Network::Groestlcoin.magic()), &[0xf9, 0xbe, 0xb4, 0xd4]);
283        assert_eq!(serialize(&Network::Testnet.magic()), &[0x0b, 0x11, 0x09, 0x07]);
284        assert_eq!(serialize(&Network::Signet.magic()), &[0x6f, 0x89, 0x2b, 0x8f]);
285        assert_eq!(serialize(&Network::Regtest.magic()), &[0xfa, 0xbf, 0xb5, 0xda]);
286
287        assert_eq!(deserialize(&[0xf9, 0xbe, 0xb4, 0xd4]).ok(), Some(Network::Groestlcoin.magic()));
288        assert_eq!(deserialize(&[0x0b, 0x11, 0x09, 0x07]).ok(), Some(Network::Testnet.magic()));
289        assert_eq!(deserialize(&[0x6f, 0x89, 0x2b, 0x8f]).ok(), Some(Network::Signet.magic()));
290        assert_eq!(deserialize(&[0xfa, 0xbf, 0xb5, 0xda]).ok(), Some(Network::Regtest.magic()));
291    }
292
293    #[test]
294    fn string_test() {
295        assert_eq!(Network::Groestlcoin.to_string(), "groestlcoin");
296        assert_eq!(Network::Testnet.to_string(), "testnet");
297        assert_eq!(Network::Regtest.to_string(), "regtest");
298        assert_eq!(Network::Signet.to_string(), "signet");
299
300        assert_eq!("groestlcoin".parse::<Network>().unwrap(), Network::Groestlcoin);
301        assert_eq!("testnet".parse::<Network>().unwrap(), Network::Testnet);
302        assert_eq!("regtest".parse::<Network>().unwrap(), Network::Regtest);
303        assert_eq!("signet".parse::<Network>().unwrap(), Network::Signet);
304        assert!("fakenet".parse::<Network>().is_err());
305    }
306
307    #[test]
308    fn service_flags_test() {
309        let all = [
310            ServiceFlags::NETWORK,
311            ServiceFlags::GETUTXO,
312            ServiceFlags::BLOOM,
313            ServiceFlags::WITNESS,
314            ServiceFlags::COMPACT_FILTERS,
315            ServiceFlags::NETWORK_LIMITED,
316        ];
317
318        let mut flags = ServiceFlags::NONE;
319        for f in all.iter() {
320            assert!(!flags.has(*f));
321        }
322
323        flags |= ServiceFlags::WITNESS;
324        assert_eq!(flags, ServiceFlags::WITNESS);
325
326        let mut flags2 = flags | ServiceFlags::GETUTXO;
327        for f in all.iter() {
328            assert_eq!(flags2.has(*f), *f == ServiceFlags::WITNESS || *f == ServiceFlags::GETUTXO);
329        }
330
331        flags2 ^= ServiceFlags::WITNESS;
332        assert_eq!(flags2, ServiceFlags::GETUTXO);
333
334        flags2 |= ServiceFlags::COMPACT_FILTERS;
335        flags2 ^= ServiceFlags::GETUTXO;
336        assert_eq!(flags2, ServiceFlags::COMPACT_FILTERS);
337
338        // Test formatting.
339        assert_eq!("ServiceFlags(NONE)", ServiceFlags::NONE.to_string());
340        assert_eq!("ServiceFlags(WITNESS)", ServiceFlags::WITNESS.to_string());
341        let flag = ServiceFlags::WITNESS | ServiceFlags::BLOOM | ServiceFlags::NETWORK;
342        assert_eq!("ServiceFlags(NETWORK|BLOOM|WITNESS)", flag.to_string());
343        let flag = ServiceFlags::WITNESS | 0xf0.into();
344        assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string());
345    }
346
347    #[test]
348    #[cfg(feature = "serde")]
349    fn serde_roundtrip() {
350        use Network::*;
351        let tests = vec![
352            (Groestlcoin, "groestlcoin"),
353            (Testnet, "testnet"),
354            (Signet, "signet"),
355            (Regtest, "regtest"),
356        ];
357
358        for tc in tests {
359            let network = tc.0;
360
361            let want = format!("\"{}\"", tc.1);
362            let got = serde_json::to_string(&tc.0).expect("failed to serialize network");
363            assert_eq!(got, want);
364
365            let back: Network = serde_json::from_str(&got).expect("failed to deserialize network");
366            assert_eq!(back, network);
367        }
368    }
369
370    #[test]
371    fn from_to_core_arg() {
372        let expected_pairs = [
373            (Network::Groestlcoin, "main"),
374            (Network::Testnet, "test"),
375            (Network::Regtest, "regtest"),
376            (Network::Signet, "signet"),
377        ];
378
379        for (net, core_arg) in &expected_pairs {
380            assert_eq!(Network::from_core_arg(core_arg), Ok(*net));
381            assert_eq!(net.to_core_arg(), *core_arg);
382        }
383    }
384
385    #[cfg(feature = "serde")]
386    #[test]
387    fn serde_as_core_arg() {
388        #[derive(Serialize, Deserialize, PartialEq, Debug)]
389        #[serde(crate = "actual_serde")]
390        struct T {
391            #[serde(with = "crate::network::as_core_arg")]
392            pub network: Network,
393        }
394
395        serde_test::assert_tokens(
396            &T { network: Network::Groestlcoin },
397            &[
398                serde_test::Token::Struct { name: "T", len: 1 },
399                serde_test::Token::Str("network"),
400                serde_test::Token::Str("main"),
401                serde_test::Token::StructEnd,
402            ],
403        );
404    }
405}