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