#![cfg(test)]
use super::target::{pow2, Words};
use crate::chain::daa::{asert, Anchor};
use crate::chain::header::encode_bits;
use crate::chain::u256::U256;
fn anchor(g: &Words) -> Anchor {
Anchor {
target: U256(*g),
time: 0,
}
}
fn simulate(genesis_target: &Words, steps: &[(u64, u64)]) -> Vec<(u64, i64, Words)> {
let mut out = Vec::new();
let (mut h, mut t) = (0u64, 0i64);
let mut target = *genesis_target;
for &(blocks, rate) in steps {
for _ in 0..blocks {
let exp = expected_hashes(&target);
let secs = ((exp + rate as u128 / 2) / rate as u128) as i64;
t += secs.max(1);
h += 1;
let a = asert(anchor(genesis_target), h, t as u64);
target = a.target.0;
out.push((h, t, target));
}
}
out
}
fn expected_hashes(target: &Words) -> u128 {
let lz = {
let mut z = 0;
for w in target.iter().rev() {
if *w == 0 {
z += 64
} else {
z += w.leading_zeros();
break;
}
}
z
};
let bits = 256 - lz; let top = if bits > 64 {
U256(*target).shr(bits - 64).0[0]
} else {
target[0]
};
let num_shift = 320 - bits;
if num_shift >= 128 {
((1u128 << 127) / top as u128) << (num_shift - 127)
} else {
(1u128 << num_shift) / top as u128
}
}
#[test]
fn asert_behaviour_and_hundredfold_step() {
let g = pow2(228);
let base_rate: u64 = (expected_hashes(&g) / 30) as u64; let steady = simulate(&g, &[(200, base_rate)]);
for (h, _, target) in &steady {
assert_eq!(encode_bits(U256(*target)), 0x1d10_0000, "height {h}");
}
let a = asert(anchor(&g), 40, 600);
let ratio_x10 = expected_hashes(&a.target.0) * 10 / expected_hashes(&g);
assert!(
(19..=21).contains(&ratio_x10),
"work ratio ×10 = {ratio_x10}"
);
assert_eq!(a.bits, 0x1d08_0000);
let run = simulate(&g, &[(1_000, 100 * base_rate)]);
let settled = run
.windows(2)
.position(|w| w[1].1 - w[0].1 >= 27)
.expect("settles");
let (h_set, t_set, _) = run[settled + 1];
eprintln!(
"asert 100x step: interval ≥ 27 s from block {h_set}, after {t_set}s wall ({} min)",
t_set / 60
);
assert!(
h_set <= 300 && t_set <= 3 * 3600,
"100x absorbed in {h_set} blocks / {t_set}s"
);
let tail = run[999].1 - run[998].1;
assert!(
(27..=33).contains(&tail),
"interval after 1000 blocks at 100x: {tail}s"
);
let both = simulate(&g, &[(1_000, 100 * base_rate), (200, base_rate)]);
let t_leave = both[999].1;
let first_slow = both[1000].1 - both[999].1;
let back = both[1000..]
.windows(2)
.find(|w| w[1].1 - w[0].1 <= 60)
.map(|w| w[1].1 - t_leave)
.expect("recovers");
eprintln!("asert 100x collapse: first block {first_slow}s ({} min), blocks ≤ 60 s again after {back}s ({} min)", first_slow / 60, back / 60);
assert!(back <= 70 * 60, "recovery took {back}s");
let g2 = pow2(232);
let r2: u64 = (expected_hashes(&g2) / 30) as u64;
let run2 = simulate(&g2, &[(20, r2), (1_000, 100 * r2)]);
let first_fast = run2[20].1 - run2[19].1;
let settled2 = run2[20..]
.windows(2)
.position(|w| w[1].1 - w[0].1 >= 27)
.expect("settles");
let (h2, t2, _) = run2[20 + settled2 + 1];
let last = run2.last().unwrap();
eprintln!(
"asert 100x step @2^232: first block {first_fast}s, interval ≥ 27 s from block {h2} ({}s wall), bits after 1000 blocks {:#x}",
t2 - run2[19].1,
encode_bits(U256(last.2))
);
}