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