use blvm_protocol::BlockHeader;
use blvm_protocol::types::Network;
pub(crate) fn ancestor_seeing_pending<'a>(
pending: &'a [(u64, u64, u64)],
stored: &'a dyn Fn(u64) -> Option<(u64, u64)>,
) -> impl Fn(u64) -> Option<(u64, u64)> + 'a {
move |height| {
if let Some((_, bits, timestamp)) = pending.iter().rev().find(|(h, _, _)| *h == height) {
return Some((*bits, *timestamp));
}
stored(height)
}
}
pub(crate) fn header_meets_required_work(
header: &BlockHeader,
height: u64,
network: Network,
ancestor: &dyn Fn(u64) -> Option<(u64, u64)>,
) -> bool {
if height == 0 {
return blvm_protocol::pow::check_proof_of_work(header).unwrap_or(false);
}
let Ok(Some(required)) =
blvm_protocol::pow::next_required_bits(network, height, header.timestamp, ancestor)
else {
return false;
};
header.bits == required && blvm_protocol::pow::check_proof_of_work(header).unwrap_or(false)
}
pub(crate) fn network_for_header_work(
protocol: Option<blvm_protocol::ProtocolVersion>,
stored: Network,
) -> Network {
protocol
.map(blvm_protocol::ProtocolVersion::consensus_network)
.unwrap_or(stored)
}
#[cfg(test)]
mod tests {
use super::*;
fn grind(mut header: BlockHeader) -> BlockHeader {
for nonce in 0..200_000 {
header.nonce = nonce;
if blvm_protocol::pow::check_proof_of_work(&header).unwrap_or(false) {
return header;
}
}
panic!("header did not meet its own target");
}
#[test]
fn easy_bits_do_not_meet_a_harder_parent() {
let parent_time = 1_600_000_000u64;
let header = grind(BlockHeader {
version: 4,
prev_block_hash: [1u8; 32],
merkle_root: [2u8; 32],
timestamp: parent_time + 1,
bits: 0x207fffff,
nonce: 0,
});
let ancestor = |h: u64| {
if h == 100 {
Some((0x1d00ffffu64, parent_time))
} else {
None
}
};
assert!(blvm_protocol::pow::check_proof_of_work(&header).unwrap_or(false));
assert!(!header_meets_required_work(
&header,
101,
Network::Mainnet,
&ancestor,
));
}
#[test]
fn second_header_uses_the_parent_accepted_in_this_batch() {
let parent_time = 1_600_000_000u64;
let stored = |h: u64| {
if h == 100 {
Some((0x207fffffu64, parent_time))
} else {
None
}
};
let first = grind(BlockHeader {
version: 4,
prev_block_hash: [1u8; 32],
merkle_root: [2u8; 32],
timestamp: parent_time + 1,
bits: 0x207fffff,
nonce: 0,
});
assert!(header_meets_required_work(
&first,
101,
Network::Mainnet,
&stored
));
let pending = [(101u64, first.bits, first.timestamp)];
let with_batch = ancestor_seeing_pending(&pending, &stored);
let second = grind(BlockHeader {
version: 4,
prev_block_hash: [3u8; 32],
merkle_root: [4u8; 32],
timestamp: parent_time + 2,
bits: 0x207fffff,
nonce: 0,
});
assert!(
header_meets_required_work(&second, 102, Network::Mainnet, &with_batch),
"the parent accepted earlier in this message supplies the required bits"
);
assert!(
!header_meets_required_work(&second, 102, Network::Mainnet, &stored),
"without that parent the next header has no required work"
);
let hard_parent = [(101u64, 0x1d00ffffu64, parent_time)];
let hard = ancestor_seeing_pending(&hard_parent, &stored);
assert!(!header_meets_required_work(
&second,
102,
Network::Mainnet,
&hard
));
}
}