1use 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#[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 Groestlcoin,
43 Testnet,
45 Signet,
47 Regtest,
49}
50
51impl Network {
52 pub fn from_magic(magic: Magic) -> Option<Network> { Network::try_from(magic).ok() }
65
66 pub fn magic(self) -> Magic { Magic::from(self) }
79
80 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 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 pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block(self) }
132
133 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 #![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#[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#[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 ChainHash::GROESTLCOIN => Ok(Network::Groestlcoin),
266 ChainHash::TESTNET => Ok(Network::Testnet),
267 ChainHash::SIGNET => Ok(Network::Signet),
268 _ => 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 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}