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