use super::*;
use crate::constants::MAX_TARGET;
fn header_hash_meets(header: &BlockHeader) -> bool {
if header.bits & 0x0080_0000 != 0 {
return false;
}
let Ok(target) = expand_target(header.bits) else {
return false;
};
if target.is_zero() {
return false;
}
let bytes = serialize_header(header);
let hash1 = Sha256::digest(bytes);
let hash2 = Sha256::digest(hash1);
let mut hash_bytes = [0u8; 32];
hash_bytes.copy_from_slice(&hash2);
let target_bytes = target.to_le_bytes();
for i in (0..32).rev() {
if hash_bytes[i] != target_bytes[i] {
return hash_bytes[i] < target_bytes[i];
}
}
true
}
#[test]
fn negative_or_zero_target_fails_and_equal_hash_passes() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1,
bits: 0x1d80_0001,
nonce: 0,
};
assert!(!check_proof_of_work(&header).unwrap());
let mut zero = header;
zero.bits = 0x0300_0000;
assert!(!check_proof_of_work(&zero).unwrap());
let bits = 0x1d00ffff;
let target = expand_target(bits).unwrap();
assert!(proof_hash_meets_target(bits, &target).unwrap());
let above = target.checked_add(U256::one()).unwrap();
assert!(!proof_hash_meets_target(bits, &above).unwrap());
}
#[test]
fn test_get_next_work_required_insufficient_headers() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
};
let prev_headers = vec![header.clone()];
let result = get_next_work_required(&header, &prev_headers);
assert!(result.is_err());
}
#[test]
fn test_get_next_work_required_normal_adjustment() {
let header1 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000,
bits: 0x1d00ffff,
nonce: 0,
};
let header2 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000 + (DIFFICULTY_ADJUSTMENT_INTERVAL * TARGET_TIME_PER_BLOCK), bits: 0x1d00ffff,
nonce: 0,
};
let prev_headers = vec![header1, header2.clone()];
let result = get_next_work_required(&header2, &prev_headers).unwrap();
assert_eq!(result, 0x1d00ffff);
}
#[test]
fn test_difficulty_from_bits() {
let d = difficulty_from_bits(0x1d00ffff).unwrap();
assert!(
(d - 1.0).abs() < 0.01,
"Genesis difficulty should be ~1.0, got {d}"
);
let d_harder = difficulty_from_bits(0x1d000800).unwrap();
assert!(d_harder > d, "Harder target should have higher difficulty");
}
#[test]
fn test_expand_target() {
let target = expand_target(0x0300ffff).unwrap(); assert!(!target.is_zero());
}
#[test]
fn test_check_proof_of_work_genesis() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x0300ffff, nonce: 0,
};
assert_eq!(
check_proof_of_work(&header).unwrap(),
header_hash_meets(&header)
);
}
#[test]
fn test_get_next_work_required_fast_blocks() {
let header1 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000,
bits: 0x1d00ffff,
nonce: 0,
};
let header2 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000 + (DIFFICULTY_ADJUSTMENT_INTERVAL * TARGET_TIME_PER_BLOCK / 2),
bits: 0x1d00ffff,
nonce: 0,
};
let prev_headers = vec![header1, header2.clone()];
let result = get_next_work_required(&header2, &prev_headers).unwrap();
assert!(result <= 0x1d00ffff);
}
#[test]
fn test_get_next_work_required_slow_blocks() {
let header1 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000,
bits: 0x1d00ffff,
nonce: 0,
};
let header2 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000 + (DIFFICULTY_ADJUSTMENT_INTERVAL * TARGET_TIME_PER_BLOCK * 2),
bits: 0x1d00ffff,
nonce: 0,
};
let prev_headers = vec![header1, header2.clone()];
let result = get_next_work_required(&header2, &prev_headers).unwrap();
assert!(result <= 0x1d00ffff);
}
#[test]
fn test_get_next_work_required_extreme_fast_blocks() {
let header1 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000,
bits: 0x1d00ffff,
nonce: 0,
};
let header2 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000 + (DIFFICULTY_ADJUSTMENT_INTERVAL * TARGET_TIME_PER_BLOCK / 14),
bits: 0x1d00ffff,
nonce: 0,
};
let prev_headers = vec![header1, header2.clone()];
let result = get_next_work_required(&header2, &prev_headers).unwrap();
assert!(result <= 0x1d00ffff);
}
#[test]
fn test_get_next_work_required_extreme_slow_blocks() {
let header1 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000,
bits: 0x1d00ffff,
nonce: 0,
};
let header2 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1000000 + (DIFFICULTY_ADJUSTMENT_INTERVAL * TARGET_TIME_PER_BLOCK * 4),
bits: 0x1d00ffff,
nonce: 0,
};
let prev_headers = vec![header1, header2.clone()];
let result = get_next_work_required(&header2, &prev_headers).unwrap();
assert!(result <= 0x1d00ffff);
}
#[test]
fn test_expand_target_zero_mantissa() {
let result = expand_target(0x1d000000).unwrap();
assert!(result.is_zero());
}
#[test]
fn test_expand_target_invalid_exponent_too_small() {
let result = expand_target(0x0200ffff);
assert!(result.is_err());
}
#[test]
fn test_expand_target_invalid_exponent_too_large() {
let result = expand_target(0x2100ffff);
assert!(result.is_err());
}
#[test]
fn test_expand_target_exponent_31() {
let result = expand_target(0x1f00ffff).unwrap(); assert!(!result.is_zero());
}
#[test]
fn test_expand_target_exponent_32_regtest_bits() {
let result = expand_target(0x2000ffff).unwrap();
assert!(!result.is_zero());
}
#[test]
fn test_gbt_target_hex_regtest_minimum_difficulty() {
let target = expand_target(0x207fffff).expect("regtest nBits");
let hex = target.gbt_target_hex();
assert_eq!(hex.len(), 64);
assert_ne!(hex, "0".repeat(64));
assert!(hex.starts_with("7fffff"));
}
#[test]
fn test_expand_target_exponent_3() {
let result = expand_target(0x0300ffff).unwrap();
assert!(!result.is_zero());
}
#[test]
fn test_expand_target_exponent_4() {
let result = expand_target(0x0400ffff).unwrap();
assert!(!result.is_zero());
}
#[test]
fn test_expand_target_exponent_29() {
let result = expand_target(0x1d00ffff).unwrap();
assert!(!result.is_zero());
}
#[test]
fn test_check_proof_of_work_invalid_target() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x0200ffff, nonce: 0,
};
let result = check_proof_of_work(&header);
assert!(result.is_err());
}
#[test]
fn test_check_proof_of_work_valid_target() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff, nonce: 0,
};
assert_eq!(
check_proof_of_work(&header).unwrap(),
header_hash_meets(&header)
);
let mut negative = header;
negative.bits |= 0x0080_0000;
assert!(!check_proof_of_work(&negative).unwrap());
}
#[test]
fn test_get_block_proof_genesis_mainnet() {
let work = get_block_proof(0x1d00ffff).unwrap();
assert_eq!(
work.gbt_target_hex(),
"0000000000000000000000000000000000000000000000000000000100010001"
);
assert!(work > U256::zero());
}
#[test]
fn test_get_block_proof_regtest_minimum_difficulty() {
let work = get_block_proof(0x207fffff).unwrap();
assert!(work > U256::zero());
assert!(work < U256::from_u128(10));
}
#[test]
fn test_u256_zero() {
let zero = U256::zero();
assert!(zero.is_zero());
}
#[test]
fn test_u256_from_u32() {
let value = U256::from_u32(0x12345678);
assert!(!value.is_zero());
}
#[test]
fn test_u256_from_u64() {
let value = U256::from_u64(0x123456789abcdef0);
assert!(!value.is_zero());
}
#[test]
fn test_u256_shl_zero_shift() {
let value = U256::from_u32(0x12345678);
let result = value.shl(0);
assert_eq!(result, value);
}
#[test]
fn test_u256_shl_large_shift() {
let value = U256::from_u32(0x12345678);
let result = value.shl(300); assert!(result.is_zero());
}
#[test]
fn test_u256_shr_zero_shift() {
let value = U256::from_u32(0x12345678);
let result = value.shr(0);
assert_eq!(result, value);
}
#[test]
fn test_u256_shr_large_shift() {
let value = U256::from_u32(0x12345678);
let result = value.shr(300); assert!(result.is_zero());
}
#[test]
fn test_u256_shl_small_shift() {
let value = U256::from_u32(0x12345678);
let result = value.shl(8);
assert!(!result.is_zero());
assert_ne!(result, value);
}
#[test]
fn test_u256_shr_small_shift() {
let value = U256::from_u32(0x12345678);
let result = value.shr(8);
assert!(!result.is_zero());
assert_ne!(result, value);
}
#[test]
fn test_u256_to_bytes() {
let value = U256::from_u32(0x12345678);
let bytes = value.to_bytes();
assert_eq!(bytes.len(), 32);
}
#[test]
fn test_u256_from_bytes() {
let mut bytes = [0u8; 32];
bytes[0] = 0x78;
bytes[1] = 0x56;
bytes[2] = 0x34;
bytes[3] = 0x12;
let value = U256::from_bytes(&bytes);
assert!(!value.is_zero());
}
#[test]
fn test_u256_ordering() {
let small = U256::from_u32(0x12345678);
let large = U256::from_u32(0x87654321);
assert!(small < large);
assert!(large > small);
assert_eq!(small.cmp(&small), std::cmp::Ordering::Equal);
}
#[test]
fn test_expand_compress_round_trip() {
let test_bits = vec![
0x1d00ffff, 0x1b0404cb, 0x0300ffff, ];
for &bits in &test_bits {
let expanded = match expand_target(bits) {
Ok(t) => t,
Err(_) => continue, };
let compressed = match compress_target(&expanded) {
Ok(b) => b,
Err(_) => {
continue;
}
};
let re_expanded = match expand_target(compressed) {
Ok(t) => t,
Err(_) => continue,
};
if re_expanded > expanded {
panic!(
"Round-trip failed for bits 0x{bits:08x}: re-expanded > original (compression should truncate, not add)"
);
}
#[allow(clippy::eq_op)]
let significant_words_match =
expanded.0[2] == re_expanded.0[2] && expanded.0[3] == re_expanded.0[3];
if !significant_words_match {
panic!(
"Round-trip failed for bits 0x{:08x}: significant bits differ (expanded: {:?}, re-expanded: {:?})",
bits, expanded.0, re_expanded.0
);
}
}
}
#[test]
fn test_compress_target_genesis() {
let genesis_bits = 0x1d00ffff;
let expanded = expand_target(genesis_bits).unwrap();
let compressed = compress_target(&expanded).unwrap();
assert!(compressed <= MAX_TARGET as u64);
assert!(compressed > 0);
let re_expanded = expand_target(compressed).unwrap();
assert_eq!(expanded, re_expanded);
}
#[test]
fn test_serialize_header() {
let header = BlockHeader {
version: 1,
prev_block_hash: [1; 32],
merkle_root: [2; 32],
timestamp: 1234567890,
bits: 0x1d00ffff,
nonce: 0x12345678,
};
let bytes = serialize_header(&header);
assert_eq!(bytes.len(), 80); }
#[test]
fn test_serialize_header_returns_fixed_80_bytes() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 0,
bits: 0,
nonce: 0,
};
let bytes: [u8; 80] = serialize_header(&header);
assert_eq!(bytes.len(), 80);
}
#[test]
fn test_serialize_header_field_layout() {
let header = BlockHeader {
version: 0x01020304,
prev_block_hash: {
let mut h = [0u8; 32];
h[0] = 0xAA;
h[31] = 0xBB;
h
},
merkle_root: {
let mut h = [0u8; 32];
h[0] = 0xCC;
h[31] = 0xDD;
h
},
timestamp: 0x05060708,
bits: 0x090A0B0C,
nonce: 0x0D0E0F10,
};
let bytes = serialize_header(&header);
assert_eq!(bytes[0], 0x04); assert_eq!(bytes[1], 0x03);
assert_eq!(bytes[2], 0x02);
assert_eq!(bytes[3], 0x01);
assert_eq!(bytes[4], 0xAA);
assert_eq!(bytes[35], 0xBB);
assert_eq!(bytes[36], 0xCC);
assert_eq!(bytes[67], 0xDD);
assert_eq!(bytes[68], 0x08);
assert_eq!(bytes[69], 0x07);
assert_eq!(bytes[70], 0x06);
assert_eq!(bytes[71], 0x05);
assert_eq!(bytes[72], 0x0C);
assert_eq!(bytes[73], 0x0B);
assert_eq!(bytes[74], 0x0A);
assert_eq!(bytes[75], 0x09);
assert_eq!(bytes[76], 0x10);
assert_eq!(bytes[77], 0x0F);
assert_eq!(bytes[78], 0x0E);
assert_eq!(bytes[79], 0x0D);
}
#[test]
fn test_serialize_header_deterministic() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0xFF; 32],
merkle_root: [0xAA; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 2083236893,
};
let bytes1 = serialize_header(&header);
let bytes2 = serialize_header(&header);
assert_eq!(bytes1, bytes2, "Header serialization must be deterministic");
}
#[test]
fn test_serialize_header_different_headers_different_bytes() {
let header1 = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
};
let mut header2 = header1.clone();
header2.nonce = 1;
let bytes1 = serialize_header(&header1);
let bytes2 = serialize_header(&header2);
assert_ne!(
bytes1, bytes2,
"Different nonces must produce different serializations"
);
assert_eq!(
bytes1[..76],
bytes2[..76],
"Non-nonce bytes should be identical"
);
assert_ne!(bytes1[76..], bytes2[76..], "Nonce bytes should differ");
}
#[test]
fn test_u256_from_bytes_simple() {
let bytes = [0u8; 32];
let value = u256_from_bytes(&bytes);
assert_eq!(value, 0);
}
#[test]
fn test_u256_from_bytes_with_data() {
let mut bytes = [0u8; 32];
bytes[0] = 0x78;
bytes[1] = 0x56;
bytes[2] = 0x34;
bytes[3] = 0x12;
let value = u256_from_bytes(&bytes);
assert_eq!(value, 0x78563412000000000000000000000000);
}
fn ancestor_from(headers: &[(u64, u64, u64)]) -> impl Fn(u64) -> Option<(u64, u64)> + '_ {
move |height| {
headers
.iter()
.find(|(h, _, _)| *h == height)
.map(|(_, bits, ts)| (*bits, *ts))
}
}
#[test]
fn off_boundary_copies_parent_bits() {
let headers = [(99u64, 0x1b012dcd, 1_000u64)];
let bits = next_required_bits(Network::Mainnet, 100, 2_000, &ancestor_from(&headers))
.unwrap()
.unwrap();
assert_eq!(bits, 0x1b012dcd);
}
#[test]
fn testnet4_retarget_uses_the_first_block_of_the_period() {
let timespan = 2016 * 600;
let headers = [
(0u64, 0x1b012dcd, 1_700_000_000u64),
(2015, 0x1d00ffff, 1_700_000_000 + timespan),
];
let lookup = ancestor_from(&headers);
let testnet3 = next_required_bits(Network::Testnet, 2016, 0, &lookup)
.unwrap()
.unwrap();
let testnet4 = next_required_bits(Network::Testnet4, 2016, 0, &lookup)
.unwrap()
.unwrap();
assert_eq!(testnet3, 0x1d00ffff);
assert_eq!(testnet4, 0x1b012dcd);
}
#[test]
fn testnet_late_block_uses_pow_limit() {
let headers = [(99u64, 0x1b012dcd, 1_000u64)];
let late = next_required_bits(
Network::Testnet,
100,
1_000 + 1_201,
&ancestor_from(&headers),
)
.unwrap()
.unwrap();
assert_eq!(late, 0x1d00ffff);
let on_time = next_required_bits(
Network::Testnet,
100,
1_000 + 1_200,
&ancestor_from(&headers),
)
.unwrap()
.unwrap();
assert_eq!(on_time, 0x1b012dcd);
}
#[test]
fn testnet_walks_back_past_minimum_difficulty_blocks() {
let headers = [
(8u64, 0x1b012dcd, 1_000u64),
(9, 0x1d00ffff, 1_600),
(10, 0x1d00ffff, 2_200),
];
let bits = next_required_bits(Network::Testnet, 11, 2_800, &ancestor_from(&headers))
.unwrap()
.unwrap();
assert_eq!(bits, 0x1b012dcd);
}
#[test]
fn regtest_does_not_retarget_and_uses_its_own_limit() {
let headers = [(143u64, 0x181bc330, 10_000u64)];
let bits = next_required_bits(Network::Regtest, 144, 20_000, &ancestor_from(&headers))
.unwrap()
.unwrap();
assert_eq!(bits, 0x181bc330);
let min = [(0u64, 0x207fffff, 1_000u64), (1, 0x207fffff, 1_600)];
let late = next_required_bits(Network::Regtest, 2, 1_600 + 1_201, &ancestor_from(&min))
.unwrap()
.unwrap();
assert_eq!(late, 0x207fffff);
let walked = next_required_bits(Network::Regtest, 2, 1_600 + 600, &ancestor_from(&min))
.unwrap()
.unwrap();
assert_eq!(walked, 0x207fffff);
}
#[test]
fn mainnet_retarget_height_112896_matches_chain_bits() {
let first_ts = 1_298_800_760u64;
let last_ts = 1_299_683_275u64;
let period_bits = 453_062_093u64;
let headers = [
(110_880u64, period_bits, first_ts),
(112_895, period_bits, last_ts),
];
let bits = next_required_bits(
Network::Mainnet,
112_896,
1_299_684_355,
&ancestor_from(&headers),
)
.unwrap()
.unwrap();
assert_eq!(bits, 453_041_201);
}
#[test]
fn genesis_has_no_required_bits() {
assert!(
next_required_bits(Network::Mainnet, 0, 1, &|_| None)
.unwrap()
.is_none()
);
}