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}