Skip to main content

blvm_protocol/
network_params.rs

1//! Network parameters for different Bitcoin variants
2//!
3//! This module defines the network-specific parameters for different
4//! Bitcoin protocol variants, including magic bytes, ports, genesis blocks,
5//! and other network-specific constants.
6
7use crate::{NetworkParameters, ProtocolVersion, Result};
8use serde::{Deserialize, Serialize};
9
10/// Network-specific constants
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct NetworkConstants {
13    /// Network magic bytes for P2P protocol identification
14    pub magic_bytes: [u8; 4],
15    /// Default P2P port for this network
16    pub default_port: u16,
17    /// Genesis block hash for this network
18    pub genesis_hash: [u8; 32],
19    /// Maximum proof-of-work target
20    pub max_target: u32,
21    /// Block subsidy halving interval
22    pub halving_interval: u64,
23    /// Network name for identification
24    pub network_name: String,
25    /// Whether this is a test network
26    pub is_testnet: bool,
27    /// DNS seeds for peer discovery
28    pub dns_seeds: Vec<String>,
29    /// Checkpoint blocks for fast sync
30    pub checkpoints: Vec<Checkpoint>,
31}
32
33/// Checkpoint block for fast synchronization
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct Checkpoint {
36    /// Block height
37    pub height: u64,
38    /// Block hash
39    pub hash: [u8; 32],
40    /// Block timestamp
41    pub timestamp: u64,
42}
43
44impl NetworkConstants {
45    /// Get constants for a specific protocol version
46    pub fn for_version(version: ProtocolVersion) -> Result<Self> {
47        match version {
48            ProtocolVersion::BitcoinV1 => Self::mainnet(),
49            ProtocolVersion::Testnet3 => Self::testnet(),
50            ProtocolVersion::Regtest => Self::regtest(),
51            ProtocolVersion::Signet => Self::signet(),
52        }
53    }
54
55    /// Bitcoin mainnet constants
56    pub fn mainnet() -> Result<Self> {
57        Ok(Self {
58            magic_bytes: [0xf9, 0xbe, 0xb4, 0xd9], // Bitcoin mainnet magic
59            default_port: 8333,
60            genesis_hash: [
61                0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63,
62                0xf7, 0x4f, 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, 0x68, 0xd6, 0x19, 0x00,
63                0x00, 0x00, 0x00, 0x00,
64            ],
65            max_target: 0x1d00ffff,
66            halving_interval: 210000,
67            network_name: "mainnet".to_string(),
68            is_testnet: false,
69            dns_seeds: vec![
70                "seed.bitcoin.sipa.be".to_string(),
71                "dnsseed.bluematt.me".to_string(),
72                "dnsseed.bitcoin.dashjr.org".to_string(),
73                "seed.bitcoinstats.com".to_string(),
74                "seed.bitcoin.jonasschnelli.ch".to_string(),
75                "seed.btc.petertodd.org".to_string(),
76            ],
77            checkpoints: Self::mainnet_checkpoints(),
78        })
79    }
80
81    /// Bitcoin testnet constants
82    pub fn testnet() -> Result<Self> {
83        Ok(Self {
84            magic_bytes: [0x0b, 0x11, 0x09, 0x07], // Bitcoin testnet magic
85            default_port: 18333,
86            genesis_hash: [
87                0x43, 0x49, 0x7f, 0xd7, 0xf8, 0x26, 0x95, 0x71, 0x08, 0xf4, 0xa3, 0x0f, 0xd9, 0xce,
88                0xc3, 0xae, 0xba, 0x79, 0x97, 0x20, 0x84, 0xe9, 0x0e, 0xad, 0x01, 0xea, 0x33, 0x09,
89                0x00, 0x00, 0x00, 0x00,
90            ],
91            max_target: 0x1d00ffff,
92            halving_interval: 210000,
93            network_name: "testnet".to_string(),
94            is_testnet: true,
95            dns_seeds: vec![
96                "testnet-seed.bitcoin.jonasschnelli.ch".to_string(),
97                "seed.tbtc.petertodd.org".to_string(),
98                "seed.testnet.bitcoin.sprovoost.nl".to_string(),
99                "testnet-seed.bluematt.me".to_string(),
100            ],
101            checkpoints: Self::testnet_checkpoints(),
102        })
103    }
104
105    /// Bitcoin regtest constants
106    pub fn regtest() -> Result<Self> {
107        Ok(Self {
108            magic_bytes: [0xfa, 0xbf, 0xb5, 0xda], // Bitcoin regtest magic
109            default_port: 18444,
110            genesis_hash: [
111                0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb,
112                0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c,
113                0xf1, 0x88, 0x91, 0x0f,
114            ],
115            max_target: 0x207fffff, // Easier difficulty for testing
116            halving_interval: 150,  // Faster halving for testing
117            network_name: "regtest".to_string(),
118            is_testnet: true,
119            dns_seeds: vec![],   // No DNS seeds for regtest
120            checkpoints: vec![], // No checkpoints for regtest
121        })
122    }
123
124    /// Bitcoin signet constants (default public signet)
125    pub fn signet() -> Result<Self> {
126        Ok(Self {
127            magic_bytes: [0x0f, 0x1b, 0xf2, 0xe1],
128            default_port: 38333,
129            genesis_hash: [
130                0xf6, 0x1e, 0xee, 0x3b, 0x63, 0xa3, 0x80, 0xa4, 0x77, 0xa0, 0x63, 0xaf, 0x32, 0xb2,
131                0xbb, 0xc9, 0x7c, 0x9f, 0xf9, 0xf0, 0x1f, 0x2c, 0x42, 0x25, 0xe9, 0x73, 0x98, 0x81,
132                0x08, 0x00, 0x00, 0x00,
133            ],
134            max_target: 0x1e0377ae,
135            halving_interval: 210_000,
136            network_name: "signet".to_string(),
137            is_testnet: true,
138            dns_seeds: vec![
139                "seed.signet.bitcoin.sprovoost.nl".to_string(),
140                "seed.signet.achownodes.xyz".to_string(),
141            ],
142            checkpoints: vec![],
143        })
144    }
145
146    /// Mainnet checkpoints for fast sync
147    fn mainnet_checkpoints() -> Vec<Checkpoint> {
148        vec![
149            Checkpoint {
150                height: 11111,
151                hash: [
152                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155                ],
156                timestamp: 1231006505,
157            },
158            // Add more checkpoints as needed
159        ]
160    }
161
162    /// Testnet checkpoints for fast sync
163    fn testnet_checkpoints() -> Vec<Checkpoint> {
164        vec![
165            Checkpoint {
166                height: 11111,
167                hash: [
168                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171                ],
172                timestamp: 1296688602,
173            },
174            // Add more checkpoints as needed
175        ]
176    }
177}
178
179impl NetworkParameters {
180    /// Create network parameters from constants
181    pub fn from_constants(constants: &NetworkConstants) -> Result<Self> {
182        let genesis_block = match constants.network_name.as_str() {
183            "testnet" => crate::genesis::testnet_genesis(),
184            "regtest" => crate::genesis::regtest_genesis(),
185            "signet" => crate::genesis::signet_genesis(),
186            _ => crate::genesis::mainnet_genesis(),
187        };
188        Ok(NetworkParameters {
189            magic_bytes: constants.magic_bytes,
190            default_port: constants.default_port,
191            genesis_block,
192            max_target: constants.max_target,
193            halving_interval: constants.halving_interval,
194            network_name: constants.network_name.clone(),
195            is_testnet: constants.is_testnet,
196            signet_challenge: None,
197        })
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_network_constants() {
207        let mainnet = NetworkConstants::mainnet().unwrap();
208        assert_eq!(mainnet.magic_bytes, [0xf9, 0xbe, 0xb4, 0xd9]);
209        assert_eq!(mainnet.default_port, 8333);
210        assert!(!mainnet.is_testnet);
211        assert!(!mainnet.dns_seeds.is_empty());
212
213        let testnet = NetworkConstants::testnet().unwrap();
214        assert_eq!(testnet.magic_bytes, [0x0b, 0x11, 0x09, 0x07]);
215        assert_eq!(testnet.default_port, 18333);
216        assert!(testnet.is_testnet);
217
218        let regtest = NetworkConstants::regtest().unwrap();
219        assert_eq!(regtest.magic_bytes, [0xfa, 0xbf, 0xb5, 0xda]);
220        assert_eq!(regtest.default_port, 18444);
221        assert!(regtest.is_testnet);
222        assert!(regtest.dns_seeds.is_empty());
223
224        let signet = NetworkConstants::signet().unwrap();
225        assert_eq!(signet.magic_bytes, [0x0f, 0x1b, 0xf2, 0xe1]);
226        assert_eq!(signet.default_port, 38333);
227        assert!(signet.is_testnet);
228        assert!(!signet.dns_seeds.is_empty());
229    }
230
231    #[test]
232    fn test_network_parameters_from_constants() {
233        let constants = NetworkConstants::mainnet().unwrap();
234        let params = NetworkParameters::from_constants(&constants).unwrap();
235
236        assert_eq!(params.magic_bytes, constants.magic_bytes);
237        assert_eq!(params.default_port, constants.default_port);
238        assert_eq!(params.network_name, constants.network_name);
239        assert_eq!(params.is_testnet, constants.is_testnet);
240    }
241
242    #[test]
243    fn test_network_constants_for_version() {
244        let mainnet = NetworkConstants::for_version(ProtocolVersion::BitcoinV1).unwrap();
245        assert_eq!(mainnet.network_name, "mainnet");
246        assert!(!mainnet.is_testnet);
247
248        let testnet = NetworkConstants::for_version(ProtocolVersion::Testnet3).unwrap();
249        assert_eq!(testnet.network_name, "testnet");
250        assert!(testnet.is_testnet);
251
252        let regtest = NetworkConstants::for_version(ProtocolVersion::Regtest).unwrap();
253        assert_eq!(regtest.network_name, "regtest");
254        assert!(regtest.is_testnet);
255
256        let signet = NetworkConstants::for_version(ProtocolVersion::Signet).unwrap();
257        assert_eq!(signet.network_name, "signet");
258        assert!(signet.is_testnet);
259    }
260
261    #[test]
262    fn test_genesis_hashes() {
263        let mainnet = NetworkConstants::mainnet().unwrap();
264        let testnet = NetworkConstants::testnet().unwrap();
265        let regtest = NetworkConstants::regtest().unwrap();
266
267        // Verify genesis hashes are different for each network
268        assert_ne!(mainnet.genesis_hash, testnet.genesis_hash);
269        assert_ne!(testnet.genesis_hash, regtest.genesis_hash);
270        assert_ne!(mainnet.genesis_hash, regtest.genesis_hash);
271
272        let signet = NetworkConstants::signet().unwrap();
273        assert_ne!(signet.genesis_hash, mainnet.genesis_hash);
274        assert_ne!(signet.genesis_hash, testnet.genesis_hash);
275        assert_ne!(signet.genesis_hash, regtest.genesis_hash);
276
277        // Verify genesis hashes are not all zeros
278        assert_ne!(mainnet.genesis_hash, [0u8; 32]);
279        assert_ne!(testnet.genesis_hash, [0u8; 32]);
280        assert_ne!(regtest.genesis_hash, [0u8; 32]);
281    }
282
283    #[test]
284    fn test_dns_seeds() {
285        let mainnet = NetworkConstants::mainnet().unwrap();
286        let testnet = NetworkConstants::testnet().unwrap();
287        let regtest = NetworkConstants::regtest().unwrap();
288
289        // Mainnet should have DNS seeds
290        assert!(!mainnet.dns_seeds.is_empty());
291        assert!(
292            mainnet
293                .dns_seeds
294                .iter()
295                .any(|seed| seed.contains("bitcoin"))
296        );
297
298        // Testnet should have DNS seeds
299        assert!(!testnet.dns_seeds.is_empty());
300        assert!(
301            testnet
302                .dns_seeds
303                .iter()
304                .any(|seed| seed.contains("testnet"))
305        );
306
307        // Regtest should have no DNS seeds
308        assert!(regtest.dns_seeds.is_empty());
309    }
310
311    #[test]
312    fn test_checkpoints() {
313        let mainnet = NetworkConstants::mainnet().unwrap();
314        let testnet = NetworkConstants::testnet().unwrap();
315        let regtest = NetworkConstants::regtest().unwrap();
316
317        // Mainnet should have checkpoints
318        assert!(!mainnet.checkpoints.is_empty());
319
320        // Testnet should have checkpoints
321        assert!(!testnet.checkpoints.is_empty());
322
323        // Regtest should have no checkpoints
324        assert!(regtest.checkpoints.is_empty());
325
326        // Check that checkpoints are ordered by height
327        for checkpoints in [&mainnet.checkpoints, &testnet.checkpoints] {
328            for i in 1..checkpoints.len() {
329                assert!(checkpoints[i].height > checkpoints[i - 1].height);
330            }
331        }
332    }
333
334    #[test]
335    fn test_max_targets() {
336        let mainnet = NetworkConstants::mainnet().unwrap();
337        let testnet = NetworkConstants::testnet().unwrap();
338        let regtest = NetworkConstants::regtest().unwrap();
339
340        // Mainnet and testnet should have same max target
341        assert_eq!(mainnet.max_target, 0x1d00ffff);
342        assert_eq!(testnet.max_target, 0x1d00ffff);
343
344        // Regtest should have easier difficulty
345        assert_eq!(regtest.max_target, 0x207fffff);
346        assert!(regtest.max_target > mainnet.max_target);
347    }
348
349    #[test]
350    fn test_halving_intervals() {
351        let mainnet = NetworkConstants::mainnet().unwrap();
352        let testnet = NetworkConstants::testnet().unwrap();
353        let regtest = NetworkConstants::regtest().unwrap();
354
355        // Mainnet and testnet should have same halving interval
356        assert_eq!(mainnet.halving_interval, 210000);
357        assert_eq!(testnet.halving_interval, 210000);
358
359        // Regtest should have faster halving for testing
360        assert_eq!(regtest.halving_interval, 150);
361        assert!(regtest.halving_interval < mainnet.halving_interval);
362    }
363
364    #[test]
365    fn test_network_constants_serialization() {
366        let mainnet = NetworkConstants::mainnet().unwrap();
367        let testnet = NetworkConstants::testnet().unwrap();
368        let regtest = NetworkConstants::regtest().unwrap();
369
370        // Test serialization/deserialization
371        for constants in [mainnet, testnet, regtest] {
372            let json = serde_json::to_string(&constants).unwrap();
373            let deserialized: NetworkConstants = serde_json::from_str(&json).unwrap();
374
375            assert_eq!(constants.magic_bytes, deserialized.magic_bytes);
376            assert_eq!(constants.default_port, deserialized.default_port);
377            assert_eq!(constants.genesis_hash, deserialized.genesis_hash);
378            assert_eq!(constants.max_target, deserialized.max_target);
379            assert_eq!(constants.halving_interval, deserialized.halving_interval);
380            assert_eq!(constants.network_name, deserialized.network_name);
381            assert_eq!(constants.is_testnet, deserialized.is_testnet);
382            assert_eq!(constants.dns_seeds, deserialized.dns_seeds);
383            assert_eq!(constants.checkpoints, deserialized.checkpoints);
384        }
385    }
386
387    #[test]
388    fn test_checkpoint_serialization() {
389        let checkpoint = Checkpoint {
390            height: 1000,
391            hash: [
392                0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02,
393                0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
394                0x01, 0x02, 0x03, 0x04,
395            ],
396            timestamp: 1234567890,
397        };
398
399        let json = serde_json::to_string(&checkpoint).unwrap();
400        let deserialized: Checkpoint = serde_json::from_str(&json).unwrap();
401
402        assert_eq!(checkpoint.height, deserialized.height);
403        assert_eq!(checkpoint.hash, deserialized.hash);
404        assert_eq!(checkpoint.timestamp, deserialized.timestamp);
405    }
406
407    #[test]
408    fn test_network_constants_equality() {
409        let mainnet1 = NetworkConstants::mainnet().unwrap();
410        let mainnet2 = NetworkConstants::mainnet().unwrap();
411        let testnet = NetworkConstants::testnet().unwrap();
412
413        assert_eq!(mainnet1, mainnet2);
414        assert_ne!(mainnet1, testnet);
415    }
416
417    #[test]
418    fn test_checkpoint_equality() {
419        let checkpoint1 = Checkpoint {
420            height: 1000,
421            hash: [0x01; 32],
422            timestamp: 1234567890,
423        };
424
425        let checkpoint2 = Checkpoint {
426            height: 1000,
427            hash: [0x01; 32],
428            timestamp: 1234567890,
429        };
430
431        let checkpoint3 = Checkpoint {
432            height: 1001,
433            hash: [0x01; 32],
434            timestamp: 1234567890,
435        };
436
437        assert_eq!(checkpoint1, checkpoint2);
438        assert_ne!(checkpoint1, checkpoint3);
439    }
440
441    #[test]
442    fn test_network_parameters_consistency() {
443        let mainnet_constants = NetworkConstants::mainnet().unwrap();
444        let mainnet_params = NetworkParameters::from_constants(&mainnet_constants).unwrap();
445
446        assert_eq!(mainnet_params.magic_bytes, mainnet_constants.magic_bytes);
447        assert_eq!(mainnet_params.default_port, mainnet_constants.default_port);
448        assert_eq!(mainnet_params.max_target, mainnet_constants.max_target);
449        assert_eq!(
450            mainnet_params.halving_interval,
451            mainnet_constants.halving_interval
452        );
453        assert_eq!(mainnet_params.network_name, mainnet_constants.network_name);
454        assert_eq!(mainnet_params.is_testnet, mainnet_constants.is_testnet);
455    }
456
457    #[test]
458    fn test_all_networks_have_unique_magic_bytes() {
459        let mainnet = NetworkConstants::mainnet().unwrap();
460        let testnet = NetworkConstants::testnet().unwrap();
461        let regtest = NetworkConstants::regtest().unwrap();
462
463        assert_ne!(mainnet.magic_bytes, testnet.magic_bytes);
464        assert_ne!(testnet.magic_bytes, regtest.magic_bytes);
465        assert_ne!(mainnet.magic_bytes, regtest.magic_bytes);
466    }
467
468    #[test]
469    fn test_all_networks_have_unique_ports() {
470        let mainnet = NetworkConstants::mainnet().unwrap();
471        let testnet = NetworkConstants::testnet().unwrap();
472        let regtest = NetworkConstants::regtest().unwrap();
473
474        assert_ne!(mainnet.default_port, testnet.default_port);
475        assert_ne!(testnet.default_port, regtest.default_port);
476        assert_ne!(mainnet.default_port, regtest.default_port);
477    }
478
479    #[test]
480    fn test_network_names() {
481        let mainnet = NetworkConstants::mainnet().unwrap();
482        let testnet = NetworkConstants::testnet().unwrap();
483        let regtest = NetworkConstants::regtest().unwrap();
484
485        assert_eq!(mainnet.network_name, "mainnet");
486        assert_eq!(testnet.network_name, "testnet");
487        assert_eq!(regtest.network_name, "regtest");
488    }
489
490    #[test]
491    fn test_testnet_flags() {
492        let mainnet = NetworkConstants::mainnet().unwrap();
493        let testnet = NetworkConstants::testnet().unwrap();
494        let regtest = NetworkConstants::regtest().unwrap();
495
496        assert!(!mainnet.is_testnet);
497        assert!(testnet.is_testnet);
498        assert!(regtest.is_testnet);
499    }
500}