1use std::fmt;
4use std::ops::Deref;
5use std::str::FromStr;
6
7use libp2p::Multiaddr;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
13#[derive(Debug, Default, Clone, PartialEq, Eq)]
14pub enum Network {
15 #[default]
17 Mainnet,
18 Arabica,
20 Mocha,
22 Custom(NetworkId),
24}
25
26#[derive(Debug, Error)]
28#[error("Invalid network id: {0}")]
29pub struct InvalidNetworkId(String);
30
31#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct NetworkId {
35 pub id: String,
37}
38
39impl NetworkId {
40 pub fn new(id: &str) -> Result<NetworkId, InvalidNetworkId> {
42 if id.contains('/') {
43 Err(InvalidNetworkId(id.to_owned()))
44 } else {
45 Ok(NetworkId { id: id.to_owned() })
46 }
47 }
48}
49
50impl AsRef<str> for NetworkId {
51 fn as_ref(&self) -> &str {
52 &self.id
53 }
54}
55
56impl Deref for NetworkId {
57 type Target = str;
58
59 fn deref(&self) -> &str {
60 &self.id
61 }
62}
63
64impl FromStr for NetworkId {
65 type Err = InvalidNetworkId;
66
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 NetworkId::new(s)
69 }
70}
71
72impl fmt::Display for NetworkId {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 f.write_str(&self.id)
75 }
76}
77
78impl Network {
79 pub fn custom(id: &str) -> Result<Network, InvalidNetworkId> {
81 Ok(Network::Custom(NetworkId::new(id)?))
82 }
83
84 pub fn is_custom(&self) -> bool {
86 matches!(self, Network::Custom(_))
87 }
88
89 pub fn id(&self) -> &str {
91 match self {
92 Network::Mainnet => "celestia",
93 Network::Arabica => "arabica-11",
94 Network::Mocha => "mocha-4",
95 Network::Custom(ref s) => &s.id,
96 }
97 }
98
99 pub fn canonical_bootnodes(&self) -> impl Iterator<Item = Multiaddr> {
101 let peers: &[_] = match self {
102 Network::Mainnet => &[
103 "/dnsaddr/da-bootstrapper-1.celestia-bootstrap.net/p2p/12D3KooWSqZaLcn5Guypo2mrHr297YPJnV8KMEMXNjs3qAS8msw8",
104 "/dnsaddr/da-bootstrapper-2.celestia-bootstrap.net/p2p/12D3KooWQpuTFELgsUypqp9N4a1rKBccmrmQVY8Em9yhqppTJcXf",
105 "/dnsaddr/da-bootstrapper-3.celestia-bootstrap.net/p2p/12D3KooWKZCMcwGCYbL18iuw3YVpAZoyb1VBGbx9Kapsjw3soZgr",
106 "/dnsaddr/da-bootstrapper-4.celestia-bootstrap.net/p2p/12D3KooWE3fmRtHgfk9DCuQFfY3H3JYEnTU3xZozv1Xmo8KWrWbK",
107 "/dnsaddr/boot.celestia.pops.one/p2p/12D3KooWBBzzGy5hAHUQVh2vBvL25CKwJ7wbmooPcz4amQhzJHJq",
108 "/dnsaddr/celestia.qubelabs.io/p2p/12D3KooWAzucnC7yawvLbmVxv53ihhjbHFSVZCsPuuSzTg6A7wgx",
109 "/dnsaddr/celestia-bootstrapper.binary.builders/p2p/12D3KooWDKvTzMnfh9j7g4RpvU6BXwH3AydTrzr1HTW6TMBQ61HF",
110 ],
111 Network::Arabica => &[
112 "/dnsaddr/da-bridge-1.celestia-arabica-11.com/p2p/12D3KooWGqwzdEqM54Dce6LXzfFr97Bnhvm6rN7KM7MFwdomfm4S",
113 "/dnsaddr/da-full-1.celestia-arabica-11.com/p2p/12D3KooWCMGM5eZWVfCN9ZLAViGfLUWAfXP5pCm78NFKb9jpBtua",
114 ],
115 Network::Mocha => &[
116 "/dnsaddr/da-bootstrapper-1-mocha-4.celestia-mocha.com/p2p/12D3KooWCBAbQbJSpCpCGKzqz3rAN4ixYbc63K68zJg9aisuAajg",
117 "/dnsaddr/da-bootstrapper-2-mocha-4.celestia-mocha.com/p2p/12D3KooWCUHPLqQXZzpTx1x3TAsdn3vYmTNDhzg66yG8hqoxGGN8",
118 "/dnsaddr/mocha-boot.pops.one/p2p/12D3KooWDzNyDSvTBdKQAmnsUdAyQCQWwM3ReXTmPaaf6LzfNwRs",
119 "/dnsaddr/celestia-mocha.qubelabs.io/p2p/12D3KooWQVmHy7JpfxpKZfLjvn12GjvMgKrWdsHkFbV2kKqQFBCG",
120 "/dnsaddr/celestia-mocha4-bootstrapper.binary.builders/p2p/12D3KooWK6AYaPSe2EP99NP5G2DKwWLfMi6zHMYdD65KRJwdJSVU",
121 "/dnsaddr/celestia-testnet-boot.01node.com/p2p/12D3KooWR923Tc8SCzweyaGZ5VU2ahyS9VWrQ8mDz56RbHjHFdzW",
122 "/dnsaddr/celestia-mocha-boot.zkv.xyz/p2p/12D3KooWFdkhm7Ac6nqNkdNiW2g16KmLyyQrqXMQeijdkwrHqQ9J",
123 ],
124 Network::Custom(_) => &[],
125 };
126 peers
127 .iter()
128 .map(|s| s.parse().expect("Invalid bootstrap address"))
129 }
130}
131
132impl FromStr for Network {
133 type Err = InvalidNetworkId;
134
135 fn from_str(value: &str) -> Result<Self, InvalidNetworkId> {
136 match value {
137 "Mainnet" | "MainNet" | "mainnet" | "celestia" => Ok(Network::Mainnet),
138 "Arabica" | "arabica" | "arabica-11" => Ok(Network::Arabica),
139 "Mocha" | "mocha" | "mocha-4" => Ok(Network::Mocha),
140 custom_id => Network::custom(custom_id),
141 }
142 }
143}
144
145impl fmt::Display for Network {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 let s = match self {
148 Network::Mainnet => "Mainnet",
149 Network::Arabica => "Arabica",
150 Network::Mocha => "Mocha",
151 Network::Custom(ref s) => s,
152 };
153
154 f.write_str(s)
155 }
156}
157
158impl<'de> Deserialize<'de> for Network {
159 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
160 where
161 D: serde::Deserializer<'de>,
162 {
163 let s = String::deserialize(deserializer)?;
164 s.parse().map_err(serde::de::Error::custom)
165 }
166}
167
168impl Serialize for Network {
169 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
170 where
171 S: serde::Serializer,
172 {
173 self.to_string().serialize(serializer)
174 }
175}
176
177impl TryFrom<String> for Network {
178 type Error = InvalidNetworkId;
179
180 fn try_from(value: String) -> Result<Self, Self::Error> {
181 value.parse()
182 }
183}
184
185impl<'a> TryFrom<&'a String> for Network {
186 type Error = InvalidNetworkId;
187
188 fn try_from(value: &'a String) -> Result<Self, Self::Error> {
189 value.parse()
190 }
191}
192
193impl<'a> TryFrom<&'a str> for Network {
194 type Error = InvalidNetworkId;
195
196 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
197 value.parse()
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_canonical_network_bootnodes() {
207 let mainnet = Network::Mainnet.canonical_bootnodes();
209 assert_ne!(mainnet.count(), 0);
210
211 let arabica = Network::Arabica.canonical_bootnodes();
212 assert_ne!(arabica.count(), 0);
213
214 let mocha = Network::Mocha.canonical_bootnodes();
215 assert_ne!(mocha.count(), 0);
216
217 let id = NetworkId::new("private").unwrap();
218 let private = Network::Custom(id).canonical_bootnodes();
219 assert_eq!(private.count(), 0);
220 }
221
222 #[test]
223 fn check_network_id() {
224 Network::custom("foo").unwrap();
225 Network::custom("foo/bar").unwrap_err();
226 }
227}