1use crate::{NetworkParameters, ProtocolVersion, Result};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct NetworkConstants {
13 pub magic_bytes: [u8; 4],
15 pub default_port: u16,
17 pub genesis_hash: [u8; 32],
19 pub max_target: u32,
21 pub halving_interval: u64,
23 pub network_name: String,
25 pub is_testnet: bool,
27 pub dns_seeds: Vec<String>,
29 pub checkpoints: Vec<Checkpoint>,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct Checkpoint {
36 pub height: u64,
38 pub hash: [u8; 32],
40 pub timestamp: u64,
42}
43
44impl NetworkConstants {
45 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 pub fn mainnet() -> Result<Self> {
58 Ok(Self {
59 magic_bytes: [0xf9, 0xbe, 0xb4, 0xd9], 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 pub fn testnet() -> Result<Self> {
86 Ok(Self {
87 magic_bytes: [0x0b, 0x11, 0x09, 0x07], 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 pub fn regtest() -> Result<Self> {
112 Ok(Self {
113 magic_bytes: [0xfa, 0xbf, 0xb5, 0xda], 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, 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![], checkpoints: vec![], })
129 }
130
131 pub fn signet() -> Result<Self> {
133 Ok(Self {
134 magic_bytes: [0x0a, 0x03, 0xcf, 0x40], 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 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 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 ]
190 }
191
192 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 ]
206 }
207}
208
209impl NetworkParameters {
210 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, [0x0a, 0x03, 0xcf, 0x40]);
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 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 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 assert!(!mainnet.dns_seeds.is_empty());
321 assert!(
322 mainnet
323 .dns_seeds
324 .iter()
325 .any(|seed| seed.contains("bitcoin"))
326 );
327
328 assert!(!testnet.dns_seeds.is_empty());
330 assert!(
331 testnet
332 .dns_seeds
333 .iter()
334 .any(|seed| seed.contains("testnet"))
335 );
336
337 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 assert!(!mainnet.checkpoints.is_empty());
349
350 assert!(!testnet.checkpoints.is_empty());
352
353 assert!(regtest.checkpoints.is_empty());
355
356 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 assert_eq!(mainnet.max_target, 0x1d00ffff);
372 assert_eq!(testnet.max_target, 0x1d00ffff);
373
374 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 assert_eq!(mainnet.halving_interval, 210000);
387 assert_eq!(testnet.halving_interval, 210000);
388
389 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 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}