khodpay_bip32/network.rs
1//! Network types and key type identifiers for BIP32 extended key serialization.
2//!
3//! This module defines network identifiers and key types used for extended key
4//! version bytes. Different networks use different version byte prefixes when
5//! serializing extended keys to Base58Check format.
6//!
7//! # Examples
8//!
9//! ```rust
10//! use khodpay_bip32::{Network, KeyType};
11//!
12//! let mainnet = Network::BitcoinMainnet;
13//! assert_eq!(mainnet.version_bytes(KeyType::Private), 0x0488ADE4);
14//! assert_eq!(mainnet.version_bytes(KeyType::Public), 0x0488B21E);
15//! ```
16
17/// Key type identifier for extended keys.
18///
19/// BIP32 defines two types of extended keys:
20/// - Private extended keys (xprv/tprv) - contain private key material
21/// - Public extended keys (xpub/tpub) - contain only public key material
22///
23/// # Examples
24///
25/// ```rust
26/// use khodpay_bip32::KeyType;
27///
28/// let private = KeyType::Private;
29/// let public = KeyType::Public;
30///
31/// assert!(private.is_private());
32/// assert!(public.is_public());
33/// ```
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum KeyType {
36 /// Private extended key (xprv/tprv).
37 ///
38 /// Contains private key material and can be used to:
39 /// - Derive child private keys
40 /// - Derive child public keys
41 /// - Sign transactions
42 Private,
43
44 /// Public extended key (xpub/tpub).
45 ///
46 /// Contains only public key material and can be used to:
47 /// - Derive child public keys (normal derivation only)
48 /// - Verify signatures
49 /// - Generate addresses for watching
50 ///
51 /// Cannot:
52 /// - Derive hardened child keys
53 /// - Sign transactions
54 Public,
55}
56
57impl KeyType {
58 /// Returns `true` if this is a private key type.
59 ///
60 /// # Examples
61 ///
62 /// ```rust
63 /// use khodpay_bip32::KeyType;
64 ///
65 /// assert!(KeyType::Private.is_private());
66 /// assert!(!KeyType::Public.is_private());
67 /// ```
68 pub fn is_private(&self) -> bool {
69 matches!(self, KeyType::Private)
70 }
71
72 /// Returns `true` if this is a public key type.
73 ///
74 /// # Examples
75 ///
76 /// ```rust
77 /// use khodpay_bip32::KeyType;
78 ///
79 /// assert!(KeyType::Public.is_public());
80 /// assert!(!KeyType::Private.is_public());
81 /// ```
82 pub fn is_public(&self) -> bool {
83 matches!(self, KeyType::Public)
84 }
85
86 /// Returns the human-readable name of the key type.
87 ///
88 /// # Examples
89 ///
90 /// ```rust
91 /// use khodpay_bip32::KeyType;
92 ///
93 /// assert_eq!(KeyType::Private.name(), "Private");
94 /// assert_eq!(KeyType::Public.name(), "Public");
95 /// ```
96 pub fn name(&self) -> &'static str {
97 match self {
98 KeyType::Private => "Private",
99 KeyType::Public => "Public",
100 }
101 }
102}
103
104impl std::fmt::Display for KeyType {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(f, "{}", self.name())
107 }
108}
109
110/// Network identifier for BIP32 extended key serialization.
111///
112/// Each network uses different version bytes when serializing extended keys.
113/// These version bytes appear as prefixes in the Base58Check encoded strings:
114///
115/// - `xprv`/`xpub` - Bitcoin Mainnet
116/// - `tprv`/`tpub` - Bitcoin Testnet
117///
118/// # Examples
119///
120/// ```rust
121/// use khodpay_bip32::Network;
122///
123/// // Create network instances
124/// let mainnet = Network::BitcoinMainnet;
125/// let testnet = Network::BitcoinTestnet;
126///
127/// // Get version bytes
128/// println!("Mainnet xprv: {:#x}", mainnet.xprv_version());
129/// println!("Testnet tprv: {:#x}", testnet.xprv_version());
130/// ```
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
132pub enum Network {
133 /// Bitcoin mainnet.
134 ///
135 /// Extended keys serialize with `xprv` (private) and `xpub` (public) prefixes.
136 ///
137 /// - Private version: `0x0488ADE4`
138 /// - Public version: `0x0488B21E`
139 BitcoinMainnet,
140
141 /// Bitcoin testnet.
142 ///
143 /// Extended keys serialize with `tprv` (private) and `tpub` (public) prefixes.
144 ///
145 /// - Private version: `0x04358394`
146 /// - Public version: `0x043587CF`
147 BitcoinTestnet,
148}
149
150impl Network {
151 /// Returns the version bytes for the specified key type.
152 ///
153 /// This is the primary method for getting version bytes, combining network
154 /// and key type information.
155 ///
156 /// # Arguments
157 ///
158 /// * `key_type` - The type of extended key (Private or Public)
159 ///
160 /// # Examples
161 ///
162 /// ```rust
163 /// use khodpay_bip32::{Network, KeyType};
164 ///
165 /// let mainnet = Network::BitcoinMainnet;
166 /// assert_eq!(mainnet.version_bytes(KeyType::Private), 0x0488ADE4);
167 /// assert_eq!(mainnet.version_bytes(KeyType::Public), 0x0488B21E);
168 /// ```
169 pub fn version_bytes(&self, key_type: KeyType) -> u32 {
170 match key_type {
171 KeyType::Private => self.xprv_version(),
172 KeyType::Public => self.xpub_version(),
173 }
174 }
175
176 /// Returns the version bytes for extended private keys (xprv/tprv).
177 ///
178 /// These 4-byte values are used as the version prefix when serializing
179 /// extended private keys to Base58Check format.
180 ///
181 /// # Returns
182 ///
183 /// - `0x0488ADE4` for Bitcoin Mainnet (xprv)
184 /// - `0x04358394` for Bitcoin Testnet (tprv)
185 ///
186 /// # Examples
187 ///
188 /// ```rust
189 /// use khodpay_bip32::Network;
190 ///
191 /// assert_eq!(Network::BitcoinMainnet.xprv_version(), 0x0488ADE4);
192 /// assert_eq!(Network::BitcoinTestnet.xprv_version(), 0x04358394);
193 /// ```
194 pub fn xprv_version(&self) -> u32 {
195 match self {
196 Network::BitcoinMainnet => 0x0488ADE4,
197 Network::BitcoinTestnet => 0x04358394,
198 }
199 }
200
201 /// Returns the version bytes for extended public keys (xpub/tpub).
202 ///
203 /// These 4-byte values are used as the version prefix when serializing
204 /// extended public keys to Base58Check format.
205 ///
206 /// # Returns
207 ///
208 /// - `0x0488B21E` for Bitcoin Mainnet (xpub)
209 /// - `0x043587CF` for Bitcoin Testnet (tpub)
210 ///
211 /// # Examples
212 ///
213 /// ```rust
214 /// use khodpay_bip32::Network;
215 ///
216 /// assert_eq!(Network::BitcoinMainnet.xpub_version(), 0x0488B21E);
217 /// assert_eq!(Network::BitcoinTestnet.xpub_version(), 0x043587CF);
218 /// ```
219 pub fn xpub_version(&self) -> u32 {
220 match self {
221 Network::BitcoinMainnet => 0x0488B21E,
222 Network::BitcoinTestnet => 0x043587CF,
223 }
224 }
225
226 /// Returns the human-readable name of the network.
227 ///
228 /// # Examples
229 ///
230 /// ```rust
231 /// use khodpay_bip32::Network;
232 ///
233 /// assert_eq!(Network::BitcoinMainnet.name(), "Bitcoin Mainnet");
234 /// assert_eq!(Network::BitcoinTestnet.name(), "Bitcoin Testnet");
235 /// ```
236 pub fn name(&self) -> &'static str {
237 match self {
238 Network::BitcoinMainnet => "Bitcoin Mainnet",
239 Network::BitcoinTestnet => "Bitcoin Testnet",
240 }
241 }
242
243 /// Attempts to identify the network from extended private key version bytes.
244 ///
245 /// This method iterates through all known networks and checks if the provided
246 /// version matches any of their xprv version bytes. This avoids hardcoding
247 /// version bytes in multiple places.
248 ///
249 /// # Arguments
250 ///
251 /// * `version` - The 4-byte version prefix from an extended private key
252 ///
253 /// # Returns
254 ///
255 /// - `Some(Network)` if the version matches a known network
256 /// - `None` if the version is not recognized
257 ///
258 /// # Examples
259 ///
260 /// ```rust
261 /// use khodpay_bip32::Network;
262 ///
263 /// assert_eq!(Network::from_xprv_version(0x0488ADE4), Some(Network::BitcoinMainnet));
264 /// assert_eq!(Network::from_xprv_version(0x04358394), Some(Network::BitcoinTestnet));
265 /// assert_eq!(Network::from_xprv_version(0xFFFFFFFF), None);
266 /// ```
267 pub fn from_xprv_version(version: u32) -> Option<Network> {
268 // Iterate through all network variants
269 const NETWORKS: [Network; 2] = [Network::BitcoinMainnet, Network::BitcoinTestnet];
270
271 NETWORKS
272 .into_iter()
273 .find(|&network| network.xprv_version() == version)
274 }
275
276 /// Attempts to identify the network from extended public key version bytes.
277 ///
278 /// This method iterates through all known networks and checks if the provided
279 /// version matches any of their xpub version bytes. This avoids hardcoding
280 /// version bytes in multiple places.
281 ///
282 /// # Arguments
283 ///
284 /// * `version` - The 4-byte version prefix from an extended public key
285 ///
286 /// # Returns
287 ///
288 /// - `Some(Network)` if the version matches a known network
289 /// - `None` if the version is not recognized
290 ///
291 /// # Examples
292 ///
293 /// ```rust
294 /// use khodpay_bip32::Network;
295 ///
296 /// assert_eq!(Network::from_xpub_version(0x0488B21E), Some(Network::BitcoinMainnet));
297 /// assert_eq!(Network::from_xpub_version(0x043587CF), Some(Network::BitcoinTestnet));
298 /// assert_eq!(Network::from_xpub_version(0xFFFFFFFF), None);
299 /// ```
300 pub fn from_xpub_version(version: u32) -> Option<Network> {
301 // Iterate through all network variants
302 const NETWORKS: [Network; 2] = [Network::BitcoinMainnet, Network::BitcoinTestnet];
303
304 NETWORKS
305 .into_iter()
306 .find(|&network| network.xpub_version() == version)
307 }
308}
309
310impl Default for Network {
311 /// Returns the default network (Bitcoin Mainnet).
312 ///
313 /// # Examples
314 ///
315 /// ```rust
316 /// use khodpay_bip32::Network;
317 ///
318 /// let network = Network::default();
319 /// assert_eq!(network, Network::BitcoinMainnet);
320 /// ```
321 fn default() -> Self {
322 Network::BitcoinMainnet
323 }
324}
325
326impl std::fmt::Display for Network {
327 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328 write!(f, "{}", self.name())
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 // KeyType tests
337 #[test]
338 fn test_key_type_is_private() {
339 assert!(KeyType::Private.is_private());
340 assert!(!KeyType::Public.is_private());
341 }
342
343 #[test]
344 fn test_key_type_is_public() {
345 assert!(KeyType::Public.is_public());
346 assert!(!KeyType::Private.is_public());
347 }
348
349 #[test]
350 fn test_key_type_name() {
351 assert_eq!(KeyType::Private.name(), "Private");
352 assert_eq!(KeyType::Public.name(), "Public");
353 }
354
355 #[test]
356 fn test_key_type_display() {
357 assert_eq!(KeyType::Private.to_string(), "Private");
358 assert_eq!(KeyType::Public.to_string(), "Public");
359 }
360
361 #[test]
362 fn test_key_type_equality() {
363 assert_eq!(KeyType::Private, KeyType::Private);
364 assert_eq!(KeyType::Public, KeyType::Public);
365 assert_ne!(KeyType::Private, KeyType::Public);
366 }
367
368 #[test]
369 fn test_key_type_clone_and_copy() {
370 let key_type1 = KeyType::Private;
371 let key_type2 = key_type1; // Copy
372 let key_type3 = key_type1; // Clone
373
374 assert_eq!(key_type1, key_type2);
375 assert_eq!(key_type1, key_type3);
376 }
377
378 // Network tests
379 #[test]
380 fn test_version_bytes_with_key_type() {
381 assert_eq!(
382 Network::BitcoinMainnet.version_bytes(KeyType::Private),
383 0x0488ADE4
384 );
385 assert_eq!(
386 Network::BitcoinMainnet.version_bytes(KeyType::Public),
387 0x0488B21E
388 );
389 assert_eq!(
390 Network::BitcoinTestnet.version_bytes(KeyType::Private),
391 0x04358394
392 );
393 assert_eq!(
394 Network::BitcoinTestnet.version_bytes(KeyType::Public),
395 0x043587CF
396 );
397 }
398
399 #[test]
400 fn test_xprv_version_bytes() {
401 assert_eq!(Network::BitcoinMainnet.xprv_version(), 0x0488ADE4);
402 assert_eq!(Network::BitcoinTestnet.xprv_version(), 0x04358394);
403 }
404
405 #[test]
406 fn test_xpub_version_bytes() {
407 assert_eq!(Network::BitcoinMainnet.xpub_version(), 0x0488B21E);
408 assert_eq!(Network::BitcoinTestnet.xpub_version(), 0x043587CF);
409 }
410
411 #[test]
412 fn test_network_names() {
413 assert_eq!(Network::BitcoinMainnet.name(), "Bitcoin Mainnet");
414 assert_eq!(Network::BitcoinTestnet.name(), "Bitcoin Testnet");
415 }
416
417 #[test]
418 fn test_from_xprv_version() {
419 assert_eq!(
420 Network::from_xprv_version(0x0488ADE4),
421 Some(Network::BitcoinMainnet)
422 );
423 assert_eq!(
424 Network::from_xprv_version(0x04358394),
425 Some(Network::BitcoinTestnet)
426 );
427 assert_eq!(Network::from_xprv_version(0xFFFFFFFF), None);
428 assert_eq!(Network::from_xprv_version(0x0488B21E), None); // xpub version, not xprv
429 }
430
431 #[test]
432 fn test_from_xpub_version() {
433 assert_eq!(
434 Network::from_xpub_version(0x0488B21E),
435 Some(Network::BitcoinMainnet)
436 );
437 assert_eq!(
438 Network::from_xpub_version(0x043587CF),
439 Some(Network::BitcoinTestnet)
440 );
441 assert_eq!(Network::from_xpub_version(0xFFFFFFFF), None);
442 assert_eq!(Network::from_xpub_version(0x0488ADE4), None); // xprv version, not xpub
443 }
444
445 #[test]
446 fn test_default_network() {
447 assert_eq!(Network::default(), Network::BitcoinMainnet);
448 }
449
450 #[test]
451 fn test_display() {
452 assert_eq!(Network::BitcoinMainnet.to_string(), "Bitcoin Mainnet");
453 assert_eq!(Network::BitcoinTestnet.to_string(), "Bitcoin Testnet");
454 }
455
456 #[test]
457 fn test_equality() {
458 assert_eq!(Network::BitcoinMainnet, Network::BitcoinMainnet);
459 assert_eq!(Network::BitcoinTestnet, Network::BitcoinTestnet);
460 assert_ne!(Network::BitcoinMainnet, Network::BitcoinTestnet);
461 }
462
463 #[test]
464 fn test_clone_and_copy() {
465 let network1 = Network::BitcoinMainnet;
466 let network2 = network1; // Copy
467 let network3 = network1; // Clone
468
469 assert_eq!(network1, network2);
470 assert_eq!(network1, network3);
471 }
472}