1pub mod address;
9pub mod constants;
10pub mod params;
11pub mod auxpow;
12
13pub use address::*;
14
15use crate::block::{Header as PureHeader, TxMerkleNode, Version};
16use crate::consensus::{encode, Decodable, Encodable};
17use crate::dogecoin::params::Params;
18use crate::internal_macros::impl_consensus_encoding;
19use crate::io::{Read, Write};
20use crate::p2p::Magic;
21use crate::prelude::*;
22use crate::{io, BlockHash, Transaction};
23use core::fmt;
24use core::ops::{Deref, DerefMut};
25use crate::dogecoin::auxpow::{AuxPow, VERSION_AUXPOW};
26
27#[derive(PartialEq, Eq, Clone, Debug)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
35pub struct Header {
36 pub pure_header: PureHeader,
38 pub aux_pow: Option<AuxPow>,
40}
41
42impl Deref for Header {
43 type Target = PureHeader;
44 fn deref(&self) -> &Self::Target {
45 &self.pure_header
46 }
47}
48
49impl DerefMut for Header {
50 fn deref_mut(&mut self) -> &mut Self::Target {
51 &mut self.pure_header
52 }
53}
54
55impl From<PureHeader> for Header {
56 fn from(pure_header: PureHeader) -> Self {
57 Self { pure_header, aux_pow: None }
58 }
59}
60
61impl Decodable for Header {
62 #[inline]
63 fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
64 r: &mut R,
65 ) -> Result<Self, encode::Error> {
66 let pure_header: PureHeader = Decodable::consensus_decode_from_finite_reader(r)?;
67 let aux_pow = if pure_header.has_auxpow_bit() {
68 Some(Decodable::consensus_decode_from_finite_reader(r)?)
69 } else {
70 None
71 };
72
73 Ok(Self { pure_header, aux_pow })
74 }
75}
76
77impl Encodable for Header {
78 #[inline]
79 fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
80 let mut len = 0;
81 len += self.pure_header.consensus_encode(w)?;
82 if let Some(ref aux_pow) = self.aux_pow {
83 len += aux_pow.consensus_encode(w)?;
84 }
85 Ok(len)
86 }
87}
88
89impl PureHeader {
90 pub fn has_auxpow_bit(&self) -> bool {
92 (self.version.to_consensus() & VERSION_AUXPOW) != 0
93 }
94
95 pub fn extract_chain_id(&self) -> i32 {
97 self.version.to_consensus() >> 16
98 }
99
100 pub fn is_legacy(&self) -> bool {
102 self.version == Version::ONE
103 || (self.version == Version::TWO && self.extract_chain_id() == 0)
105 }
106
107 pub fn extract_base_version(&self) -> i32 {
109 self.version.to_consensus() % VERSION_AUXPOW
110 }
111}
112
113#[derive(PartialEq, Eq, Clone, Debug)]
128#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
130pub struct Block {
131 pub header: Header,
133 pub txdata: Vec<Transaction>,
135}
136
137impl Block {
138 pub fn block_hash(&self) -> BlockHash { self.header.block_hash() }
140
141 pub fn block_hash_with_scrypt(&self) -> BlockHash { self.header.block_hash_with_scrypt() }
143
144 pub fn check_merkle_root(&self) -> bool {
146 match self.compute_merkle_root() {
147 Some(merkle_root) => self.header.merkle_root == merkle_root,
148 None => false,
149 }
150 }
151
152 pub fn compute_merkle_root(&self) -> Option<TxMerkleNode> {
154 let hashes = self
155 .txdata
156 .iter()
157 .map(|obj| obj.compute_txid().to_raw_hash());
158 crate::merkle_tree::calculate_root(hashes).map(|h| h.into())
159 }
160}
161
162impl_consensus_encoding!(Block, header, txdata);
163
164#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
166#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
167#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
168#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
169#[non_exhaustive]
170pub enum Network {
171 Dogecoin,
173 Testnet,
175 Regtest,
177}
178
179impl Network {
180 pub const fn params(self) -> &'static Params {
182 match self {
183 Network::Dogecoin => &Params::DOGECOIN,
184 Network::Testnet => &Params::TESTNET,
185 Network::Regtest => &Params::REGTEST,
186 }
187 }
188
189 pub fn magic(self) -> Magic {
191 match self {
192 Network::Dogecoin => Magic::from_bytes([0xC0, 0xC0, 0xC0, 0xC0]),
193 Network::Testnet => Magic::from_bytes([0xFC, 0xC1, 0xB7, 0xDC]),
194 Network::Regtest => Magic::from_bytes([0xFA, 0xBF, 0xB5, 0xDA]),
195 }
196 }
197}
198
199impl AsRef<Params> for Network {
200 fn as_ref(&self) -> &Params {
201 self.params()
202 }
203}
204
205impl fmt::Display for Network {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 match self {
208 Network::Dogecoin => write!(f, "dogecoin"),
209 Network::Testnet => write!(f, "testnet"),
210 Network::Regtest => write!(f, "regtest"),
211 }
212 }
213}
214
215impl core::str::FromStr for Network {
216 type Err = crate::network::ParseNetworkError;
217
218 fn from_str(s: &str) -> Result<Self, Self::Err> {
219 match s {
220 "dogecoin" => Ok(Network::Dogecoin),
221 "testnet" => Ok(Network::Testnet),
222 "regtest" => Ok(Network::Regtest),
223 _ => Err(crate::network::ParseNetworkError(s.to_owned())),
224 }
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use hex::{test_hex_unwrap as hex};
231 use hashes::Hash;
232 use super::*;
233 use crate::block::{ValidationError, Version};
234 use crate::consensus::encode::{deserialize, serialize};
235 use crate::{CompactTarget, Target, Work};
236 use crate::{Network as BitcoinNetwork};
237
238 #[test]
239 fn dogecoin_block_test() {
240 let some_block = hex!("01000000c76fe7f8ec09989d32b7907966fbd347134f80a7b71efce55fec502aa126ba3894b3065289ff8ba1ab4e8391771174d47cf2c974ebd24a1bdafd6c107d5a7a207d78bb52de8f001c00da8c3c0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2602bc6d062f503253482f047178bb5208f8042975030000000d2f7374726174756d506f6f6c2f000000000100629b29c45500001976a91450e9fe87c705dcd4b7523b47e3314c2115f5d5df88ac0000000001000000015f48fabf4425324df2b5e58f4e9c771297f76f5fa37db7556f6fc1d22742da1f010000006a473044022062d29d2d26f7d826e7b72257486e294d284832743c7803a2901eb07e326b25a002207efc391b0f4e724c9d518075c0e056cc425540f845b0fd419ba8a9d49d69288301210297a2568525760a98454d84f5e5adba9fd0a41726a6fb774ddc407279e41e2061ffffffff0240bab598200000001976a91401348a2b83aeb6b1ba2a174a1a40b7c75fbeb12088ac0040be40250000001976a914025407d928ef333979d064ae233353d80e29d58c88ac00000000");
242 let cutoff_block = hex!("01000000c76fe7f8ec09989d32b7907966fbd347134f80a7b71efce55fec502aa126ba3894b3065289ff8ba1ab4e8391771174d47cf2c974ebd24a1bdafd6c107d5a7a207d78bb52de8f001c00da8c3c0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2602bc6d062f503253482f047178bb5208f8042975030000000d2f7374726174756d506f6f6c2f000000000100629b29c45500001976a91450e9fe87c705dcd4b7523b47e3314c2115f5d5df88ac0000000001000000015f48fabf4425324df2b5e58f4e9c771297f76f5fa37db7556f6fc1d22742da1f010000006a473044022062d29d2d26f7d826e7b72257486e294d284832743c7803a2901eb07e326b25a002207efc391b0f4e724c9d518075c0e056cc425540f845b0fd419ba8a9d49d69288301210297a2568525760a98454d84f5e5adba9fd0a41726a6fb774ddc407279e41e2061ffffffff0240bab598200000001976a91401348a2b83aeb6b1ba2a174a1a40b7c75fbeb12088ac0040be40250000001976a914025407d928ef333979d064ae233353d80e29d58c88ac");
243 let header = &some_block[0..80];
244
245 let currhash = hex!("02000e08452370e171f4b9d2d8617f091f2bd53c35a537073ec3d9800bc89457");
246 let prevhash = hex!("c76fe7f8ec09989d32b7907966fbd347134f80a7b71efce55fec502aa126ba38");
247 let merkle = hex!("94b3065289ff8ba1ab4e8391771174d47cf2c974ebd24a1bdafd6c107d5a7a20");
248 let work = Work::from(0x1c788001c78_u128);
249
250 let decode: Result<Block, _> = deserialize(&some_block);
251 let bad_decode: Result<Block, _> = deserialize(&cutoff_block);
252
253 assert!(decode.is_ok());
254 assert!(bad_decode.is_err());
255 let real_decode = decode.unwrap();
256 assert_eq!(serialize(&real_decode.header.block_hash()), currhash);
257 assert_eq!(real_decode.header.version, Version::ONE);
258 assert_eq!(serialize(&real_decode.header.prev_blockhash), prevhash);
259 assert_eq!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap());
260 assert_eq!(serialize(&real_decode.header.merkle_root), merkle);
261 assert_eq!(real_decode.header.time, 1388017789);
262 assert_eq!(real_decode.header.bits, CompactTarget::from_consensus(469798878));
263 assert_eq!(real_decode.header.nonce, 1015863808);
264 assert_eq!(real_decode.header.work(), work);
265 assert_eq!(
266 real_decode.header.validate_pow_with_scrypt(real_decode.header.target()).unwrap(),
267 real_decode.block_hash_with_scrypt()
268 );
269 assert_eq!(real_decode.header.difficulty(BitcoinNetwork::Bitcoin), 455);
272 assert_eq!(real_decode.header.difficulty_float(), 455.52430084170516);
273
274 assert!(!real_decode.header.has_auxpow_bit());
275 assert_eq!(real_decode.header.extract_chain_id(), 0);
276 assert_eq!(real_decode.header.extract_base_version(), 1);
277 assert!(real_decode.header.is_legacy());
278
279 assert!(real_decode.header.aux_pow.is_none());
280
281 assert_eq!(serialize(&real_decode.header.pure_header), header);
282 assert_eq!(serialize(&real_decode.header), header);
283 assert_eq!(serialize(&real_decode), some_block);
284 }
285
286 #[test]
287 fn dogecoin_block_test_with_auxpow() {
288 let block = hex!("020162001e21ad14bc1ef20cf2d58e2b755ae4a7bfb75c906c74ef3dbb97cc57dcd77581b14423c43517df2b4f3277731daba29d0d865b515a6f19f5fb61d5799b28f2c9b48713540fa8071b0000000001000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4f032ac209fabe6d6dd3ea48350b102b90acf9eac6629072d5f697c02faf360b26d365e7b2bfb980700100000000000000062f503253482f04ce871354080811312915000000092f7374726174756d2f000000000100f2052a010000001976a914f332ec6f1729495e7edcd8ce9d887742567fe60988ac0000000006d2bbd93141ea6d2c8434caeb01828a2a522275b66d2b21fe4ed8230cfe65a101ad2f07c348abdc05f57e2e7d8763488aad71df9f557aec46ae0207ae2bb74a1500000000000000000002000000f288b555ed9b44c814afbbbac135d95e0984a5cc7cb554fccbd2ca27c5e423cebf3021ed058ac83eaa0f64b0d405fc99216209b7e56deeeefceb3629210d1cabcb8713545a50021bc40227b50301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d0300b1050101062f503253482fffffffff010085fd36af050000232102c9cbaeb767cc8c884204601f322c6977890cdd3d274f8b1a704ae00382102191ac0000000001000000011fcccc77028fc5fb96d60ee5f258da271ba9b4fdec12594a5dd2efa1fb5bd14b010000006b483045022100f4a17664176706f74877433dbfb8e68a5c1e45730da9248a8da3bab833cea1ca022018fee77621c20f196b82c3c8fd663959b0c8ea985b0407bdbac1414a5a5a21bf0121033fcc1cb9c1b7b11758eb2cd3a25b4ff917a5e248f5d6fcc74160dd6a450acf8bffffffff020759dd6e000000001976a914a553c686ac1aa534ffcb3b694c463944175a6f3c88ac8548f276890700001976a914a1ea13863020f36897b671ad328d98e9364f12b488ac000000000100000001215c45fc31d3beae4a5c76efbb0925d0b4bace72f62248aa3777927eecb42152000000006b483045022100bd3f18da6acd8180ed99c7c7fc6feab18653008cc58be5c32a470193aea330860220719c64121805606240c48dab2e8e3f3d82501b78b78597043ca134abf4c85443012103e6435d9ad2a3f3ff2d2c58aef41075df4eb5417c313d3ea4d5bb87ab46241940ffffffff0140ab8aac140000001976a91481db1aa49ebc6a71cad96949eb28e22af85eb0bd88ac00000000");
290 let header = &block[0..398];
291 let pure_header = &header[0..80];
292 let auxpow = &header[80..];
293 let auxpow_coinbase_tx = &auxpow[..164];
294 let auxpow_parent_hash = &auxpow[164..196];
295 let auxpow_coinbase_branch = &auxpow[196..229];
296 let auxpow_coinbase_index = &auxpow[229..233];
297 let auxpow_blockchain_branch = &auxpow[233..234];
298 let auxpow_blockchain_index = &auxpow[234..238];
299 let auxpow_parent_block_header = &auxpow[238..];
300
301 let currhash = hex!("7080b9bfb2e765d3260b36af2fc097f6d5729062c6eaf9ac902b100b3548ead3");
302 let prevhash = hex!("1e21ad14bc1ef20cf2d58e2b755ae4a7bfb75c906c74ef3dbb97cc57dcd77581");
303 let merkle = hex!("b14423c43517df2b4f3277731daba29d0d865b515a6f19f5fb61d5799b28f2c9");
304
305 let decode: Result<Block, _> = deserialize(&block);
306 assert!(decode.is_ok());
307 let block_decode = decode.unwrap();
308
309 assert_eq!(serialize(&block_decode.header.block_hash()), currhash);
310 assert_eq!(block_decode.header.version, Version::from_consensus(6422786));
311 assert_eq!(serialize(&block_decode.header.prev_blockhash), prevhash);
312 assert_eq!(block_decode.header.merkle_root, block_decode.compute_merkle_root().unwrap());
313 assert_eq!(serialize(&block_decode.header.merkle_root), merkle);
314 assert_eq!(block_decode.header.time, 1410566068);
315 assert_eq!(block_decode.header.bits, CompactTarget::from_consensus(453486607));
316 assert_eq!(block_decode.header.nonce, 0);
317
318 assert_eq!(
320 block_decode.header.validate_pow_with_scrypt(block_decode.header.target()),
321 Err(ValidationError::BadProofOfWork)
322 );
323 assert_eq!(block_decode.header.difficulty(BitcoinNetwork::Bitcoin), 8559);
326 assert_eq!(block_decode.header.difficulty_float(), 8559.417587564147);
327
328 assert!(block_decode.header.has_auxpow_bit());
329 assert_eq!(block_decode.header.extract_chain_id(), 98);
330 assert_eq!(block_decode.header.extract_base_version(), 2);
331 assert!(!block_decode.header.is_legacy());
332
333 assert!(block_decode.header.aux_pow.is_some());
334 let auxpow_decode = block_decode.header.aux_pow.as_ref().unwrap();
335 assert_eq!(serialize(&auxpow_decode.coinbase_tx), auxpow_coinbase_tx);
336 assert_eq!(auxpow_decode.parent_hash.to_byte_array(), auxpow_parent_hash);
337 assert_eq!(serialize(&auxpow_decode.coinbase_branch), auxpow_coinbase_branch);
338 assert_eq!(auxpow_decode.coinbase_index.to_le_bytes(), auxpow_coinbase_index);
339 assert_eq!(serialize(&auxpow_decode.blockchain_branch), auxpow_blockchain_branch);
340 assert_eq!(auxpow_decode.blockchain_index.to_le_bytes(), auxpow_blockchain_index);
341 assert_eq!(serialize(&auxpow_decode.parent_block_header), auxpow_parent_block_header);
342
343 assert_eq!(serialize(&auxpow_decode), auxpow);
344 assert_eq!(serialize(&block_decode.header.pure_header), pure_header);
345 assert_eq!(serialize(&block_decode.header), header);
346 assert_eq!(serialize(&block_decode), block);
347 }
348
349 #[test]
350 fn validate_pow_with_scrypt_test() {
351 let some_header = hex!("01000000c76fe7f8ec09989d32b7907966fbd347134f80a7b71efce55fec502aa126ba3894b3065289ff8ba1ab4e8391771174d47cf2c974ebd24a1bdafd6c107d5a7a207d78bb52de8f001c00da8c3c");
352 let some_header: Header =
353 deserialize(&some_header).expect("Can't deserialize correct block header");
354 assert_eq!(
355 some_header.validate_pow_with_scrypt(some_header.target()).unwrap(),
356 some_header.block_hash_with_scrypt()
357 );
358
359 match some_header.validate_pow_with_scrypt(Target::ZERO) {
361 Err(ValidationError::BadTarget) => (),
362 _ => panic!("unexpected result from validate_pow_with_scrypt"),
363 }
364
365 let mut invalid_header: Header = some_header;
367 invalid_header.nonce += 1;
368 match invalid_header.validate_pow_with_scrypt(invalid_header.target()) {
369 Err(ValidationError::BadProofOfWork) => (),
370 _ => panic!("unexpected result from validate_pow_with_scrypt"),
371 }
372 }
373
374 #[test]
375 fn block_hash_with_scrypt_test() {
376 struct Test {
377 input: Vec<u8>,
378 output: Vec<u8>,
379 output_str: &'static str,
380 }
381
382 let tests = vec![
383 Test {
385 input: hex!("01000000f615f7ce3b4fc6b8f61e8f89aedb1d0852507650533a9e3b10b9bbcc30639f279fcaa86746e1ef52d3edb3c4ad8259920d509bd073605c9bf1d59983752a6b06b817bb4ea78e011d012d59d4"),
386 output: vec![217, 235, 134, 99, 255, 236, 36, 28, 47, 177, 24, 173, 183, 222, 151, 168, 44, 128, 59, 111, 244, 109, 87, 102, 121, 53, 200, 16, 1, 0, 0, 0],
387 output_str: "0000000110c8357966576df46f3b802ca897deb7ad18b12f1c24ecff6386ebd9"
388 },
389 Test {
391 input: hex!("020000004c1271c211717198227392b029a64a7971931d351b387bb80db027f270411e398a07046f7d4a08dd815412a8712f874a7ebf0507e3878bd24e20a3b73fd750a667d2f451eac7471b00de6659"),
392 output: vec![6, 88, 152, 215, 171, 45, 170, 130, 53, 205, 218, 149, 17, 210, 72, 243, 1, 11, 94, 17, 246, 130, 248, 7, 65, 239, 43, 0, 0, 0, 0, 0],
393 output_str: "00000000002bef4107f882f6115e0b01f348d21195dacd3582aa2dabd7985806",
394 },
395 Test {
396 input: hex!("0200000011503ee6a855e900c00cfdd98f5f55fffeaee9b6bf55bea9b852d9de2ce35828e204eef76acfd36949ae56d1fbe81c1ac9c0209e6331ad56414f9072506a77f8c6faf551eac7471b00389d01"),
397 output: vec![148, 252, 136, 28, 159, 241, 218, 80, 210, 53, 237, 40, 242, 187, 207, 221, 254, 183, 8, 78, 99, 235, 213, 189, 17, 13, 58, 0, 0, 0, 0, 0],
398 output_str: "00000000003a0d11bdd5eb634e08b7feddcfbbf228ed35d250daf19f1c88fc94",
399 },
400 Test {
401 input: hex!("02000000a72c8a177f523946f42f22c3e86b8023221b4105e8007e59e81f6beb013e29aaf635295cb9ac966213fb56e046dc71df5b3f7f67ceaeab24038e743f883aff1aaafaf551eac7471b0166249b"),
402 output: vec![129, 202, 168, 20, 81, 221, 248, 101, 156, 242, 175, 216, 89, 157, 45, 108, 138, 114, 68, 50, 225, 136, 242, 149, 248, 64, 11, 0, 0, 0, 0, 0],
403 output_str: "00000000000b40f895f288e13244728a6c2d9d59d8aff29c65f8dd5114a8ca81",
404 },
405 Test {
406 input: hex!("010000007824bc3a8a1b4628485eee3024abd8626721f7f870f8ad4d2f33a27155167f6a4009d1285049603888fe85a84b6c803a53305a8d497965a5e896e1a00568359589faf551eac7471b0065434e"),
407 output: vec![254, 5, 225, 151, 24, 24, 134, 106, 220, 126, 142, 110, 47, 215, 232, 216, 153, 30, 3, 35, 73, 205, 145, 88, 0, 7, 48, 0, 0, 0, 0, 0],
408 output_str: "00000000003007005891cd4923031e99d8e8d72f6e8e7edc6a86181897e105fe",
409 },
410 Test {
411 input: hex!("0200000050bfd4e4a307a8cb6ef4aef69abc5c0f2d579648bd80d7733e1ccc3fbc90ed664a7f74006cb11bde87785f229ecd366c2d4e44432832580e0608c579e4cb76f383f7f551eac7471b00c36982"),
412 output: vec![140, 236, 0, 56, 77, 114, 199, 231, 79, 91, 52, 13, 115, 175, 2, 250, 71, 203, 12, 19, 199, 175, 164, 38, 180, 240, 24, 0, 0, 0, 0, 0],
413 output_str: "000000000018f0b426a4afc7130ccb47fa02af730d345b4fe7c7724d3800ec8c",
414 },
415 ];
416
417 for test in tests {
418 let header: Header =
419 deserialize(&test.input).expect("Can't deserialize correct block header");
420 assert_eq!(header.block_hash_with_scrypt().to_string(), test.output_str);
421 assert_eq!(serialize(&header.block_hash_with_scrypt()), test.output);
422 }
423 }
424
425 #[test]
426 fn max_target_from_compact() {
427 let bits = 0x1e0fffff_u32;
429 let want = Target::MAX_ATTAINABLE_MAINNET_DOGE;
430 let got = Target::from_compact(CompactTarget::from_consensus(bits));
431 assert_eq!(got, want)
432 }
433
434 #[test]
435 fn compact_target_from_adjustment_is_max_target() {
436 let height = 480;
437 let params = Params::new(Network::Dogecoin);
438 let starting_bits = CompactTarget::from_consensus(0x1e0fffff); let timespan = 4 * params.pow_target_timespan(height); let got = CompactTarget::from_next_work_required_dogecoin(starting_bits, timespan, ¶ms, height);
441 let want = params.max_attainable_target.to_compact_lossy();
442 assert_eq!(got, want);
443 }
444
445 #[test]
446 fn compact_target_from_adjustment_is_max_target_digishield() {
447 let height = 145_000;
448 let params = Params::new(Network::Dogecoin);
449 let starting_bits = CompactTarget::from_consensus(0x1e0fffff); let timespan = 5 * params.pow_target_timespan(height); let got = CompactTarget::from_next_work_required_dogecoin(starting_bits, timespan, ¶ms, height);
452 let want = params.max_attainable_target.to_compact_lossy();
453 assert_eq!(got, want);
454 }
455
456 #[test]
457 fn compact_target_from_minimum_downward_difficulty_adjustment() {
458 let pre_digishield_heights = vec![5_000, 10_000, 15_000];
459 let digishield_heights = vec![145_000, 1_000_000];
460 let starting_bits = CompactTarget::from_consensus(0x1b02f5b6); let params = Params::new(Network::Dogecoin);
462 for height in pre_digishield_heights {
463 let timespan = 4 * params.pow_target_timespan(height); let got = CompactTarget::from_next_work_required_dogecoin(starting_bits, timespan, ¶ms, height);
465 let want = Target::from_compact(starting_bits)
466 .max_transition_threshold_dogecoin(¶ms, height)
467 .to_compact_lossy();
468 assert_eq!(got, want);
469 }
470 for height in digishield_heights {
471 let timespan = 5 * params.pow_target_timespan(height); let got = CompactTarget::from_next_work_required_dogecoin(starting_bits, timespan, ¶ms, height);
473 let want = Target::from_compact(starting_bits)
474 .max_transition_threshold_dogecoin(¶ms, height)
475 .to_compact_lossy();
476 assert_eq!(got, want);
477 }
478 }
479
480 #[test]
481 fn should_compute_target() {
482 #[derive(Debug)]
483 struct TestCase<'a> {
484 name: &'a str,
485 height: u32,
486 starting_bits: CompactTarget,
487 start_time: i64,
488 end_time: i64,
489 expected_adjustment_bits: CompactTarget,
490 }
491
492 let test_cases = vec![
493 TestCase {
494 name: "Downwards difficulty adjustment (timespan: 150,098 s, slower than expected)",
495 height: 240,
496 starting_bits: CompactTarget::from_consensus(0x1e0ffff0), start_time: 1386325540, end_time: 1386475638, expected_adjustment_bits: CompactTarget::from_consensus(0x1e0fffff) },
501 TestCase {
503 name: "Downwards difficulty adjustment Digishield (timespan: 252 s, slower than expected)",
504 height: 145_000,
505 starting_bits: CompactTarget::from_consensus(0x1b499dfd), start_time: 1395094427, end_time: 1395094679, expected_adjustment_bits: CompactTarget::from_consensus(0x1b671062), },
510 TestCase {
512 name: "Downwards difficulty adjustment Digishield (timespan: 525 s, slower than expected)",
513 height: 145_000,
514 starting_bits: CompactTarget::from_consensus(0x1b3439cd), start_time: 1395100835, end_time: 1395101360, expected_adjustment_bits: CompactTarget::from_consensus(0x1b4e56b3), },
519 TestCase {
520 name: "Downwards difficulty adjustment Digishield (timespan: 225 s, slower than expected)",
521 height: 1_131_290,
522 starting_bits: CompactTarget::from_consensus(0x1b01cf5d), start_time: 1458248044, end_time: 1458248269, expected_adjustment_bits: CompactTarget::from_consensus(0x1b0269d1) },
527 TestCase {
528 name: "Downwards difficulty adjustment Digishield (timespan: 77 s, slower than expected)",
529 height: 1_531_886,
530 starting_bits: CompactTarget::from_consensus(0x1b01c45a), start_time: 1483302792, end_time: 1483302869, expected_adjustment_bits: CompactTarget::from_consensus(0x1b01d36e) },
535 TestCase {
536 name: "Upwards difficulty adjustment (timespan: 202 s, faster than expected)",
537 height: 480,
538 starting_bits: CompactTarget::from_consensus(0x1e0fffff), start_time: 1386475638, end_time: 1386475840, expected_adjustment_bits: CompactTarget::from_consensus(0x1e00ffff) },
543 TestCase {
545 name: "Upwards difficulty adjustment (timespan: 12,105 s, faster than expected)",
546 height: 0,
547 starting_bits: CompactTarget::from_consensus(0x1c1a1206), start_time: 1386942008, end_time: 1386954113, expected_adjustment_bits: CompactTarget::from_consensus(0x1c15ea59), },
552 TestCase {
554 name: "Upwards difficulty adjustment Digishield (timespan: -70 s, faster than expected)",
555 height: 145_000,
556 starting_bits: CompactTarget::from_consensus(0x1b446f21), start_time: 1395380517, end_time: 1395380447, expected_adjustment_bits: CompactTarget::from_consensus(0x1b335358), },
561 TestCase {
562 name: "Upwards difficulty adjustment Digishield (timespan: 8 s, faster than expected)",
563 height: 1_131_286,
564 starting_bits: CompactTarget::from_consensus(0x1b029d4f), start_time: 1458247987, end_time: 1458247995, expected_adjustment_bits: CompactTarget::from_consensus(0x1b025a60) },
569 TestCase {
570 name: "Upwards difficulty adjustment Digishield (timespan: 36 s, faster than expected)",
571 height: 1_531_882,
572 starting_bits: CompactTarget::from_consensus(0x1b01dc29), start_time: 1483302572, end_time: 1483302608, expected_adjustment_bits: CompactTarget::from_consensus(0x1b01c45a) },
577 TestCase {
579 name: "Difficulty adjustment rounding Digishield (timespan: 48 s, faster than expected)",
580 height: 145_000,
581 starting_bits: CompactTarget::from_consensus(0x1b671062), start_time: 1395094679, end_time: 1395094727, expected_adjustment_bits: CompactTarget::from_consensus(0x1b6558a4), },
586 ];
587
588 let params = Params::new(Network::Dogecoin);
589
590 for test_case in test_cases.iter() {
592 let timespan = test_case.end_time - test_case.start_time;
593 let adjustment = CompactTarget::from_next_work_required_dogecoin(
594 test_case.starting_bits,
595 timespan,
596 ¶ms,
597 test_case.height,
598 );
599 assert_eq!(
600 adjustment, test_case.expected_adjustment_bits,
601 "Unexpected adjustment bits for test case: {}",
602 test_case.name
603 );
604 }
605
606 for test_case in test_cases.iter() {
608 let start_header = PureHeader {
609 version: Version::ONE,
610 prev_blockhash: BlockHash::all_zeros(),
611 merkle_root: TxMerkleNode::all_zeros(),
612 time: test_case.start_time as u32,
613 bits: CompactTarget::from_consensus(0x1e0fffff), nonce: 0
615 }.into();
616 let end_header = PureHeader {
617 version: Version::ONE,
618 prev_blockhash: BlockHash::all_zeros(),
619 merkle_root: TxMerkleNode::all_zeros(),
620 time: test_case.end_time as u32,
621 bits: test_case.starting_bits,
622 nonce: 0
623 }.into();
624 let adjustment = CompactTarget::from_header_difficulty_adjustment_dogecoin(
625 start_header,
626 end_header,
627 ¶ms,
628 test_case.height
629 );
630 assert_eq!(
631 adjustment, test_case.expected_adjustment_bits,
632 "Unexpected adjustment bits for test case using headers: {}",
633 test_case.name
634 );
635 }
636 }
637
638 #[test]
639 fn compact_target_from_maximum_upward_difficulty_adjustment() {
640 let pre_digishield_heights = vec![5_000, 10_000, 15_000];
641 let digishield_heights = vec![145_000, 1_000_000];
642 let starting_bits = CompactTarget::from_consensus(0x1b025a60); let params = Params::new(Network::Dogecoin);
644 for height in pre_digishield_heights {
645 let timespan = (0.06 * params.pow_target_timespan(height) as f64) as i64; let got = CompactTarget::from_next_work_required_dogecoin(starting_bits, timespan, ¶ms, height);
647 let want = Target::from_compact(starting_bits)
648 .min_transition_threshold_dogecoin(¶ms, height)
649 .to_compact_lossy();
650 assert_eq!(got, want);
651 }
652 for height in digishield_heights {
653 let timespan = -params.pow_target_timespan(height); let got = CompactTarget::from_next_work_required_dogecoin(starting_bits, timespan, ¶ms, height);
655 let want = Target::from_compact(starting_bits)
656 .min_transition_threshold_dogecoin(¶ms, height)
657 .to_compact_lossy();
658 assert_eq!(got, want);
659 }
660 }
661
662 #[test]
663 fn roundtrip_compact_target() {
664 let consensus = 0x1e0f_ffff;
665 let compact = CompactTarget::from_consensus(consensus);
666 let t = Target::from_compact(CompactTarget::from_consensus(consensus));
667 assert_eq!(t, Target::from(compact)); let back = t.to_compact_lossy();
670 assert_eq!(back, compact); assert_eq!(back.to_consensus(), consensus);
673 }
674}