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!(mainnet
247            .dns_seeds
248            .iter()
249            .any(|seed| seed.contains("bitcoin")));
250
251        // Testnet should have DNS seeds
252        assert!(!testnet.dns_seeds.is_empty());
253        assert!(testnet
254            .dns_seeds
255            .iter()
256            .any(|seed| seed.contains("testnet")));
257
258        // Regtest should have no DNS seeds
259        assert!(regtest.dns_seeds.is_empty());
260    }
261
262    #[test]
263    fn test_checkpoints() {
264        let mainnet = NetworkConstants::mainnet().unwrap();
265        let testnet = NetworkConstants::testnet().unwrap();
266        let regtest = NetworkConstants::regtest().unwrap();
267
268        // Mainnet should have checkpoints
269        assert!(!mainnet.checkpoints.is_empty());
270
271        // Testnet should have checkpoints
272        assert!(!testnet.checkpoints.is_empty());
273
274        // Regtest should have no checkpoints
275        assert!(regtest.checkpoints.is_empty());
276
277        // Check that checkpoints are ordered by height
278        for checkpoints in [&mainnet.checkpoints, &testnet.checkpoints] {
279            for i in 1..checkpoints.len() {
280                assert!(checkpoints[i].height > checkpoints[i - 1].height);
281            }
282        }
283    }
284
285    #[test]
286    fn test_max_targets() {
287        let mainnet = NetworkConstants::mainnet().unwrap();
288        let testnet = NetworkConstants::testnet().unwrap();
289        let regtest = NetworkConstants::regtest().unwrap();
290
291        // Mainnet and testnet should have same max target
292        assert_eq!(mainnet.max_target, 0x1d00ffff);
293        assert_eq!(testnet.max_target, 0x1d00ffff);
294
295        // Regtest should have easier difficulty
296        assert_eq!(regtest.max_target, 0x207fffff);
297        assert!(regtest.max_target > mainnet.max_target);
298    }
299
300    #[test]
301    fn test_halving_intervals() {
302        let mainnet = NetworkConstants::mainnet().unwrap();
303        let testnet = NetworkConstants::testnet().unwrap();
304        let regtest = NetworkConstants::regtest().unwrap();
305
306        // Mainnet and testnet should have same halving interval
307        assert_eq!(mainnet.halving_interval, 210000);
308        assert_eq!(testnet.halving_interval, 210000);
309
310        // Regtest should have faster halving for testing
311        assert_eq!(regtest.halving_interval, 150);
312        assert!(regtest.halving_interval < mainnet.halving_interval);
313    }
314
315    #[test]
316    fn test_network_constants_serialization() {
317        let mainnet = NetworkConstants::mainnet().unwrap();
318        let testnet = NetworkConstants::testnet().unwrap();
319        let regtest = NetworkConstants::regtest().unwrap();
320
321        // Test serialization/deserialization
322        for constants in [mainnet, testnet, regtest] {
323            let json = serde_json::to_string(&constants).unwrap();
324            let deserialized: NetworkConstants = serde_json::from_str(&json).unwrap();
325
326            assert_eq!(constants.magic_bytes, deserialized.magic_bytes);
327            assert_eq!(constants.default_port, deserialized.default_port);
328            assert_eq!(constants.genesis_hash, deserialized.genesis_hash);
329            assert_eq!(constants.max_target, deserialized.max_target);
330            assert_eq!(constants.halving_interval, deserialized.halving_interval);
331            assert_eq!(constants.network_name, deserialized.network_name);
332            assert_eq!(constants.is_testnet, deserialized.is_testnet);
333            assert_eq!(constants.dns_seeds, deserialized.dns_seeds);
334            assert_eq!(constants.checkpoints, deserialized.checkpoints);
335        }
336    }
337
338    #[test]
339    fn test_checkpoint_serialization() {
340        let checkpoint = Checkpoint {
341            height: 1000,
342            hash: [
343                0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02,
344                0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
345                0x01, 0x02, 0x03, 0x04,
346            ],
347            timestamp: 1234567890,
348        };
349
350        let json = serde_json::to_string(&checkpoint).unwrap();
351        let deserialized: Checkpoint = serde_json::from_str(&json).unwrap();
352
353        assert_eq!(checkpoint.height, deserialized.height);
354        assert_eq!(checkpoint.hash, deserialized.hash);
355        assert_eq!(checkpoint.timestamp, deserialized.timestamp);
356    }
357
358    #[test]
359    fn test_network_constants_equality() {
360        let mainnet1 = NetworkConstants::mainnet().unwrap();
361        let mainnet2 = NetworkConstants::mainnet().unwrap();
362        let testnet = NetworkConstants::testnet().unwrap();
363
364        assert_eq!(mainnet1, mainnet2);
365        assert_ne!(mainnet1, testnet);
366    }
367
368    #[test]
369    fn test_checkpoint_equality() {
370        let checkpoint1 = Checkpoint {
371            height: 1000,
372            hash: [0x01; 32],
373            timestamp: 1234567890,
374        };
375
376        let checkpoint2 = Checkpoint {
377            height: 1000,
378            hash: [0x01; 32],
379            timestamp: 1234567890,
380        };
381
382        let checkpoint3 = Checkpoint {
383            height: 1001,
384            hash: [0x01; 32],
385            timestamp: 1234567890,
386        };
387
388        assert_eq!(checkpoint1, checkpoint2);
389        assert_ne!(checkpoint1, checkpoint3);
390    }
391
392    #[test]
393    fn test_network_parameters_consistency() {
394        let mainnet_constants = NetworkConstants::mainnet().unwrap();
395        let mainnet_params = NetworkParameters::from_constants(&mainnet_constants).unwrap();
396
397        assert_eq!(mainnet_params.magic_bytes, mainnet_constants.magic_bytes);
398        assert_eq!(mainnet_params.default_port, mainnet_constants.default_port);
399        assert_eq!(mainnet_params.max_target, mainnet_constants.max_target);
400        assert_eq!(
401            mainnet_params.halving_interval,
402            mainnet_constants.halving_interval
403        );
404        assert_eq!(mainnet_params.network_name, mainnet_constants.network_name);
405        assert_eq!(mainnet_params.is_testnet, mainnet_constants.is_testnet);
406    }
407
408    #[test]
409    fn test_all_networks_have_unique_magic_bytes() {
410        let mainnet = NetworkConstants::mainnet().unwrap();
411        let testnet = NetworkConstants::testnet().unwrap();
412        let regtest = NetworkConstants::regtest().unwrap();
413
414        assert_ne!(mainnet.magic_bytes, testnet.magic_bytes);
415        assert_ne!(testnet.magic_bytes, regtest.magic_bytes);
416        assert_ne!(mainnet.magic_bytes, regtest.magic_bytes);
417    }
418
419    #[test]
420    fn test_all_networks_have_unique_ports() {
421        let mainnet = NetworkConstants::mainnet().unwrap();
422        let testnet = NetworkConstants::testnet().unwrap();
423        let regtest = NetworkConstants::regtest().unwrap();
424
425        assert_ne!(mainnet.default_port, testnet.default_port);
426        assert_ne!(testnet.default_port, regtest.default_port);
427        assert_ne!(mainnet.default_port, regtest.default_port);
428    }
429
430    #[test]
431    fn test_network_names() {
432        let mainnet = NetworkConstants::mainnet().unwrap();
433        let testnet = NetworkConstants::testnet().unwrap();
434        let regtest = NetworkConstants::regtest().unwrap();
435
436        assert_eq!(mainnet.network_name, "mainnet");
437        assert_eq!(testnet.network_name, "testnet");
438        assert_eq!(regtest.network_name, "regtest");
439    }
440
441    #[test]
442    fn test_testnet_flags() {
443        let mainnet = NetworkConstants::mainnet().unwrap();
444        let testnet = NetworkConstants::testnet().unwrap();
445        let regtest = NetworkConstants::regtest().unwrap();
446
447        assert!(!mainnet.is_testnet);
448        assert!(testnet.is_testnet);
449        assert!(regtest.is_testnet);
450    }
451}