extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use core::num::NonZeroUsize;
use crate::kairos::Kairos;
use crate::metis::{
Cut, Dot, DotSet, EpochAddress, EpochBootstrapError, EpochRefusal, Epochs, LineageProofEntry,
LineageProofRecord, SealRecord, SealedEpoch, Stability, VersionVector, Vouched,
};
use super::wire::lifecycle::displaced_window;
use crate::metis::dot::RawDot;
#[track_caller]
fn d(station: u32, counter: u64) -> Dot {
Dot::from_parts(station, counter).expect("test literal names the non-dot counter zero")
}
fn address(generation: u64, dot: (u32, u64)) -> EpochAddress {
EpochAddress::try_from_parts(generation, dot).unwrap()
}
fn cut(entries: &[(u32, u64)]) -> Cut {
let mut vector = VersionVector::new();
for &(station, counter) in entries {
vector.observe(station, counter);
}
Cut::from_witnessed(vector)
}
fn proof(seals: Vec<SealRecord>) -> LineageProofRecord {
LineageProofRecord::try_new(
seals
.into_iter()
.map(|seal| LineageProofEntry::new(seal, [0u8; 32]))
.collect(),
)
.unwrap()
}
fn seal_record(
winner: (u64, (u32, u64)),
candidates: &[(u64, (u32, u64))],
ledger: &[(u32, u64)],
join: &[(u32, u64)],
) -> SealRecord {
let mut frame = vec![0x01];
frame.extend_from_slice(&winner.0.to_be_bytes());
frame.extend_from_slice(&winner.1.0.to_be_bytes());
frame.extend_from_slice(&winner.1.1.to_be_bytes());
frame.extend_from_slice(&u32::try_from(candidates.len()).unwrap().to_be_bytes());
for &(generation, dot) in candidates {
frame.extend_from_slice(&generation.to_be_bytes());
frame.extend_from_slice(&dot.0.to_be_bytes());
frame.extend_from_slice(&dot.1.to_be_bytes());
}
frame.extend_from_slice(&u32::try_from(ledger.len()).unwrap().to_be_bytes());
for &(station, counter) in ledger {
frame.extend_from_slice(&station.to_be_bytes());
frame.extend_from_slice(&counter.to_be_bytes());
}
frame.extend_from_slice(&DotSet::from_witnessed(&cut(join)).to_bytes());
SealRecord::from_bytes(&frame).expect("the fixture frame is structurally canonical")
}
fn declaration(
roster: &[u32],
dot: Dot,
tick: u64,
at: &[(u32, u64)],
) -> crate::metis::Declaration {
let witnessed = cut(at);
let mut stability = Stability::new(roster.iter().copied());
for &station in roster {
stability.report_cut(station, &witnessed).unwrap();
}
let mut epochs = Epochs::new(roster.iter().copied(), NonZeroUsize::new(1).unwrap());
epochs
.declare(
dot,
Kairos::new(tick, 0u16, 1, 0u16),
&stability,
&Cut::bottom(),
)
.expect("the fixture dot is fresh above the fixture cut")
}
#[test]
fn test_bootstrap_equals_the_exporting_machine() {
let (exporter, sealed) = displaced_window();
let lineage = proof(vec![SealRecord::from_sealed(&sealed)]);
let joiner = Epochs::bootstrap([1, 2], NonZeroUsize::new(2).unwrap(), &lineage).unwrap();
assert_eq!(joiner, exporter);
}
#[test]
fn test_bootstrap_recognizer_holds_the_duplicate_contract() {
let (_, sealed) = displaced_window();
let epoch = sealed.declaration();
let lineage = proof(vec![SealRecord::from_sealed(&sealed)]);
let mut joiner = Epochs::bootstrap([1, 2], NonZeroUsize::new(2).unwrap(), &lineage).unwrap();
assert_eq!(joiner.recognize(epoch, d(1, 3)), Ok(()));
assert_eq!(
joiner.recognize(epoch, d(2, 4)),
Err(EpochRefusal::AddressMiss {
epoch,
dot: RawDot::new(2, 4)
})
);
let foreign = address(1, (1, 3));
assert_eq!(
joiner.recognize(foreign, d(1, 1)),
Err(EpochRefusal::BeyondHorizon { epoch: foreign })
);
let replay = declaration(&[1, 2], d(2, 3), 7, &[(1, 2), (2, 2)]);
let stability = Stability::new([1, 2]);
assert_eq!(joiner.deliver(replay, &stability, &Cut::bottom()), Ok(()));
assert_eq!(
joiner.confirm(epoch, &Vouched::trust(1, cut(&[(1, 3), (2, 3)]))),
Ok(())
);
assert_eq!(joiner.adopt_report(epoch, &Vouched::trust(2, 3)), Ok(()));
}
#[test]
fn test_bootstrap_continues_the_lifecycle() {
let (_, sealed) = displaced_window();
let lineage = proof(vec![SealRecord::from_sealed(&sealed)]);
let mut joiner = Epochs::bootstrap([1, 2], NonZeroUsize::new(2).unwrap(), &lineage).unwrap();
let mut stability = Stability::new([1, 2]);
let at = cut(&[(1, 3), (2, 3)]);
for station in [1, 2] {
stability.report_cut(station, &at).unwrap();
}
let next = joiner
.declare(
d(1, 9),
Kairos::new(11, 0u16, 1, 0u16),
&stability,
&Cut::bottom(),
)
.expect("the joiner's watermark licenses the next declaration");
assert_eq!(next.address().generation(), 2);
let delivered = cut(&[(1, 9), (2, 3)]);
let mut progressed = Stability::new([1, 2]);
for station in [1, 2] {
progressed.report_cut(station, &delivered).unwrap();
joiner
.confirm(next.address(), &Vouched::trust(station, delivered.clone()))
.unwrap();
}
let _ = joiner.adopt(1, 9, &progressed).expect("confirmed");
joiner
.adopt_report(next.address(), &Vouched::trust(2, 3))
.unwrap();
let second = joiner.try_seal(&progressed).expect("seals").clone();
assert_eq!(second.declaration().generation(), 2);
let grown = proof(vec![
SealRecord::from_sealed(&sealed),
SealRecord::from_sealed(&second),
]);
let second_joiner = Epochs::bootstrap([1, 2], NonZeroUsize::new(2).unwrap(), &grown).unwrap();
assert_eq!(second_joiner, joiner);
}
#[test]
fn test_bootstrap_then_snapshot_round_trips() {
let (_, sealed) = displaced_window();
let lineage = proof(vec![SealRecord::from_sealed(&sealed)]);
let joiner = Epochs::bootstrap([1, 2], NonZeroUsize::new(2).unwrap(), &lineage).unwrap();
let restored =
Epochs::rehydrate(&joiner.snapshot(), [1, 2], NonZeroUsize::new(2).unwrap()).unwrap();
assert_eq!(restored, joiner);
}
#[test]
fn test_bootstrap_admits_a_truncated_suffix() {
let five = seal_record((5, (1, 4)), &[(5, (1, 4))], &[(1, 4)], &[(1, 4), (2, 2)]);
let six = seal_record((6, (2, 5)), &[(6, (2, 5))], &[(2, 5)], &[(1, 4), (2, 5)]);
let lineage = proof(vec![five, six]);
let joiner = Epochs::bootstrap([1, 2], NonZeroUsize::new(3).unwrap(), &lineage).unwrap();
assert_eq!(joiner.sealed().count(), 2);
assert_eq!(joiner.recognize(address(6, (2, 5)), d(1, 4)), Ok(()));
let evicted = address(4, (1, 1));
assert_eq!(
joiner.recognize(evicted, d(1, 1)),
Err(EpochRefusal::BeyondHorizon { epoch: evicted })
);
}
#[test]
fn test_bootstrap_refuses_the_empty_proof() {
let empty = LineageProofRecord::try_new(Vec::new()).unwrap();
assert_eq!(
Epochs::bootstrap([1, 2], NonZeroUsize::new(2).unwrap(), &empty),
Err(EpochBootstrapError::EmptyProof)
);
}
#[test]
fn test_bootstrap_refuses_past_the_horizon() {
let one = seal_record((1, (1, 1)), &[(1, (1, 1))], &[(1, 1)], &[(1, 1)]);
let two = seal_record((2, (1, 2)), &[(2, (1, 2))], &[(1, 2)], &[(1, 2)]);
let lineage = proof(vec![one, two]);
assert_eq!(
Epochs::bootstrap([1], NonZeroUsize::new(1).unwrap(), &lineage),
Err(EpochBootstrapError::HorizonExceeded {
generations: 2,
horizon: 1
})
);
}
#[test]
fn test_bootstrap_refuses_the_generation_ceiling() {
for newest in [u64::MAX, u64::MAX - 1] {
let ceiling = seal_record((newest, (1, 1)), &[(newest, (1, 1))], &[(1, 1)], &[(1, 1)]);
let lineage = proof(vec![ceiling]);
assert_eq!(
Epochs::bootstrap([1], NonZeroUsize::new(1).unwrap(), &lineage),
Err(EpochBootstrapError::CeilingGeneration),
"newest generation {newest} must refuse"
);
}
}
#[test]
fn test_the_ceiling_window_never_seals() {
let near = seal_record(
(u64::MAX - 2, (1, 1)),
&[(u64::MAX - 2, (1, 1))],
&[(1, 1)],
&[(1, 1)],
);
let lineage = proof(vec![near]);
let mut joiner = Epochs::bootstrap([1], NonZeroUsize::new(3).unwrap(), &lineage).unwrap();
let drive = |epochs: &mut Epochs, dot: Dot, tick: u64| {
let mut stability = Stability::new([1]);
stability
.report_cut(1, &cut(&[(1, dot.counter() - 1)]))
.unwrap();
let declaration = epochs
.declare(
dot,
Kairos::new(tick, 0u16, 1, 0u16),
&stability,
&Cut::bottom(),
)
.expect("licensed");
let delivered = cut(&[(1, dot.counter())]);
let mut progressed = Stability::new([1]);
progressed.report_cut(1, &delivered).unwrap();
epochs
.confirm(declaration.address(), &Vouched::trust(1, delivered.clone()))
.unwrap();
let _ = epochs
.adopt(1, dot.counter(), &progressed)
.expect("confirmed");
epochs.try_seal(&progressed).map(SealedEpoch::declaration)
};
let sealed = drive(&mut joiner, d(1, 2), 5);
assert_eq!(sealed.map(EpochAddress::generation), Some(u64::MAX - 1));
assert_eq!(drive(&mut joiner, d(1, 3), 7), None);
assert!(joiner.adopted(), "the round itself completed");
assert_eq!(
joiner
.sealed()
.last()
.map(|sealed| sealed.declaration().generation()),
Some(u64::MAX - 1),
"lineage is untouched by the refused ceiling seal"
);
}
#[test]
fn test_bootstrap_refuses_a_foreign_join_station() {
let crafted = seal_record((1, (1, 1)), &[(1, (1, 1))], &[(1, 1)], &[(1, 1), (9, 1)]);
let lineage = proof(vec![crafted]);
assert_eq!(
Epochs::bootstrap([1, 2], NonZeroUsize::new(1).unwrap(), &lineage),
Err(EpochBootstrapError::ForeignStation {
generation: 1,
station: 9
})
);
}
#[test]
fn test_bootstrap_refuses_a_candidate_outside_its_generation() {
let crafted = seal_record((1, (1, 1)), &[(2, (1, 1))], &[(1, 1)], &[(1, 1)]);
let lineage = proof(vec![crafted]);
assert_eq!(
Epochs::bootstrap([1], NonZeroUsize::new(1).unwrap(), &lineage),
Err(EpochBootstrapError::CandidateOutsideGeneration {
generation: 1,
found: 2
})
);
}
#[test]
fn test_bootstrap_refuses_a_foreign_candidate_station() {
let crafted = seal_record(
(1, (1, 1)),
&[(1, (1, 1)), (1, (9, 1))],
&[(1, 1)],
&[(1, 1)],
);
let lineage = proof(vec![crafted]);
assert_eq!(
Epochs::bootstrap([1, 2], NonZeroUsize::new(1).unwrap(), &lineage),
Err(EpochBootstrapError::ForeignStation {
generation: 1,
station: 9
})
);
}
#[test]
fn test_bootstrap_refuses_a_foreign_ledger_station() {
let crafted = seal_record((1, (1, 1)), &[(1, (1, 1))], &[(1, 1), (9, 1)], &[(1, 1)]);
let lineage = proof(vec![crafted]);
assert_eq!(
Epochs::bootstrap([1, 2], NonZeroUsize::new(1).unwrap(), &lineage),
Err(EpochBootstrapError::ForeignStation {
generation: 1,
station: 9
})
);
}
#[test]
fn test_bootstrap_refuses_a_winner_outside_its_candidates() {
let crafted = seal_record((1, (1, 1)), &[], &[(1, 1)], &[(1, 1)]);
let lineage = proof(vec![crafted]);
assert_eq!(
Epochs::bootstrap([1], NonZeroUsize::new(1).unwrap(), &lineage),
Err(EpochBootstrapError::WinnerNotCandidate {
generation: 1,
winner: d(1, 1)
})
);
}
#[test]
fn test_bootstrap_refuses_an_uncovered_ledger_dot() {
let crafted = seal_record((1, (1, 1)), &[(1, (1, 1))], &[(1, 1), (1, 3)], &[(1, 1)]);
let lineage = proof(vec![crafted]);
assert_eq!(
Epochs::bootstrap([1], NonZeroUsize::new(1).unwrap(), &lineage),
Err(EpochBootstrapError::UncoveredLedgerDot {
generation: 1,
dot: d(1, 3)
})
);
}