use spine::Hasher;
use crate::root::{CouplingProof, VerifierConfig, validate_committed_epochs};
#[must_use]
pub fn verify_epoch_evolution(
old_epochs: &[(u64, Vec<(u64, u64)>)],
old_size: u64,
new_epochs: &[(u64, Vec<(u64, u64)>)],
new_size: u64,
) -> bool {
if old_size > new_size {
return false;
}
if !validate_committed_epochs(old_epochs, old_size)
|| !validate_committed_epochs(new_epochs, new_size)
{
return false;
}
let lookup = |timeline: &[(u64, Vec<(u64, u64)>)], id: u64| {
timeline
.binary_search_by_key(&id, |&(tid, _)| tid)
.ok()
.map(|i| timeline[i].1.clone())
};
for (id, old) in old_epochs {
let Some(new) = lookup(new_epochs, *id) else {
return false;
};
if new.len() < old.len() {
return false;
}
let last = old.len() - 1;
if new[..last] != old[..last] {
return false;
}
let (o_start, o_end) = old[last];
let (n_start, n_end) = new[last];
if n_start != o_start {
return false;
}
if o_end == u64::MAX {
if n_end != u64::MAX && n_end < old_size {
return false;
}
} else if n_end != o_end {
return false;
}
if new[old.len()..].iter().any(|&(start, _)| start < old_size) {
return false;
}
}
for (id, new) in new_epochs {
if lookup(old_epochs, *id).is_none()
&& new.first().is_some_and(|&(start, _)| start < old_size)
{
return false;
}
}
true
}
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn verify_consistency_with_coupling(
hasher: &dyn Hasher,
alg_id: u64,
old_size: u64,
new_size: u64,
arity: u64,
boundary_hash: &[u8],
peak_path: &[spine::ProofStep],
new_peaks: &[Vec<u8>],
split_index: usize,
old_coupling: &CouplingProof,
new_coupling: &CouplingProof,
old_combined_root: &[u8],
new_combined_root: &[u8],
old_expected_active_algs: &[u64],
new_expected_active_algs: &[u64],
config: VerifierConfig,
) -> bool {
let old_res = old_coupling.verify(
hasher,
alg_id,
old_size,
arity,
old_combined_root,
old_expected_active_algs,
config,
);
let new_res = new_coupling.verify(
hasher,
alg_id,
new_size,
arity,
new_combined_root,
new_expected_active_algs,
config,
);
match (old_res, new_res) {
(Some(old_raw_root), Some(new_raw_root)) => cml::verify_consistency(
hasher,
old_size,
new_size,
arity,
boundary_hash,
peak_path,
new_peaks,
split_index,
&old_raw_root,
&new_raw_root,
),
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
const MAX: u64 = u64::MAX;
#[test]
fn test_verify_epoch_evolution() {
let old = vec![(0u64, vec![(0u64, MAX)])];
assert!(verify_epoch_evolution(&old, 5, &[(0, vec![(0, MAX)])], 9));
assert!(verify_epoch_evolution(&old, 5, &[(0, vec![(0, 5)])], 9));
assert!(verify_epoch_evolution(&old, 5, &[(0, vec![(0, 7)])], 9));
assert!(verify_epoch_evolution(
&old,
5,
&[(0, vec![(0, 6), (8, MAX)])],
9
));
assert!(verify_epoch_evolution(
&old,
5,
&[(0, vec![(0, MAX)]), (1, vec![(5, MAX)])],
9
));
assert!(!verify_epoch_evolution(&old, 5, &[(0, vec![(1, MAX)])], 9));
assert!(!verify_epoch_evolution(&old, 5, &[(0, vec![(0, 4)])], 9));
assert!(!verify_epoch_evolution(&old, 5, &[(1, vec![(5, MAX)])], 9));
assert!(!verify_epoch_evolution(
&[(0, vec![(0, 2)])],
5,
&[(0, vec![(0, 2), (3, MAX)])],
9
));
assert!(!verify_epoch_evolution(
&old,
5,
&[(0, vec![(0, MAX)]), (1, vec![(2, MAX)])],
9
));
assert!(!verify_epoch_evolution(
&[(0, vec![(0, 3)])],
5,
&[(0, vec![(0, 4)])],
9
));
assert!(!verify_epoch_evolution(&old, 9, &old, 5));
}
}