use zakura_chain::block::{self, Height};
pub(crate) fn tip_child_mismatch(
previous_block_hash: block::Hash,
block_height: Height,
best_tip: Option<(Height, block::Hash)>,
) -> Option<Height> {
let (tip_height, tip_hash) = best_tip?;
if previous_block_hash != tip_hash {
return None;
}
let expected_height = Height(tip_height.0.saturating_add(1));
(block_height != expected_height).then_some(expected_height)
}
#[cfg(test)]
pub(crate) fn poison_coinbase_height(
canonical: &zakura_chain::block::Block,
height: Height,
) -> std::sync::Arc<zakura_chain::block::Block> {
use std::sync::Arc;
use zakura_chain::transparent;
let mut poisoned = canonical.clone();
let coinbase = Arc::make_mut(
poisoned
.transactions
.first_mut()
.expect("test block has a coinbase transaction"),
);
match coinbase
.inputs_mut()
.first_mut()
.expect("coinbase transaction has an input")
{
transparent::Input::Coinbase {
height: coinbase_height,
..
} => *coinbase_height = height,
transparent::Input::PrevOut { .. } => panic!("the first input is a coinbase input"),
}
Arc::new(poisoned)
}
#[cfg(test)]
mod tests {
use super::*;
const TIP_HASH: block::Hash = block::Hash([0xAA; 32]);
const OTHER_HASH: block::Hash = block::Hash([0xBB; 32]);
#[test]
fn tip_child_with_the_expected_height_is_accepted() {
assert_eq!(
tip_child_mismatch(TIP_HASH, Height(101), Some((Height(100), TIP_HASH))),
None,
);
}
#[test]
fn tip_child_with_a_rewritten_low_height_is_a_mismatch() {
assert_eq!(
tip_child_mismatch(TIP_HASH, Height(1), Some((Height(100), TIP_HASH))),
Some(Height(101)),
"a height rewritten far behind the tip must be reported, \
not left to the behind-tip policy"
);
}
#[test]
fn tip_child_with_a_rewritten_high_height_is_a_mismatch() {
assert_eq!(
tip_child_mismatch(TIP_HASH, Height(500_000), Some((Height(100), TIP_HASH))),
Some(Height(101)),
);
}
#[test]
fn a_block_that_is_not_a_tip_child_is_not_checked() {
assert_eq!(
tip_child_mismatch(OTHER_HASH, Height(1), Some((Height(100), TIP_HASH))),
None,
"without a known parent the height can't be authenticated from the tip alone",
);
}
#[test]
fn no_tip_means_no_check() {
assert_eq!(tip_child_mismatch(TIP_HASH, Height(1), None), None);
}
}