extern crate alloc;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::vec::Vec;
use core::num::NonZeroUsize;
#[cfg(test)]
use proptest::prelude::*;
use super::fabric::Fabric;
use super::replica::Replica;
use super::tape::{Cursor, Delivery, assert_quiescent};
use super::{act, assert_converged, crash, fleet_of, lineage_of, resync};
const ROSTER: [u32; 3] = [1, 2, 3];
const MAX_OPS: usize = 40;
const CANDIDATES: [u32; 2] = [4, 5];
fn horizon() -> NonZeroUsize {
NonZeroUsize::new(2).expect("positive")
}
fn live(fleet: &BTreeMap<u32, Replica>, evicted: &BTreeSet<u32>) -> Vec<u32> {
fleet
.keys()
.copied()
.filter(|station| !evicted.contains(station))
.collect()
}
fn roster_of(replica: &Replica) -> Vec<u32> {
replica.epochs().roster().collect()
}
fn oldest_ground(replica: &Replica) -> Vec<u32> {
let epochs = replica.epochs();
let admitted: BTreeSet<u32> = epochs
.sealed()
.filter_map(|sealed| sealed.admission())
.flat_map(crate::metis::Admission::joiners)
.collect();
epochs
.roster()
.filter(|station| !admitted.contains(station))
.collect()
}
fn install_admitted(
fabric: &mut Fabric,
fleet: &mut BTreeMap<u32, Replica>,
evicted: &BTreeSet<u32>,
) -> bool {
let mut installed = false;
for candidate in CANDIDATES {
if fleet.contains_key(&candidate) {
continue;
}
let sponsor = live(fleet, evicted).into_iter().find(|station| {
fleet[station]
.epochs()
.sealed()
.filter_map(|sealed| sealed.admission())
.any(|admission| admission.admits(candidate))
});
let Some(sponsor) = sponsor else {
continue;
};
let Some(checkpoint) = fleet[&sponsor].joiner_checkpoint_for(candidate) else {
continue;
};
let ground = oldest_ground(&fleet[&sponsor]);
let lineage = lineage_of(&fleet[&sponsor]);
let joiner = Replica::bootstrap(checkpoint, &ground, horizon(), &lineage)
.expect("an admitted joiner bootstraps from the proof that admits it");
fabric.admit(candidate);
let _ = fleet.insert(candidate, joiner);
resync(fabric, fleet, candidate);
installed = true;
}
installed
}
#[allow(clippy::too_many_lines)]
fn interpret_with(tape: &[u8], delivery: Delivery) {
let mut seed = [0u8; 8];
for (slot, byte) in seed.iter_mut().zip(tape.iter()) {
*slot = *byte;
}
let mut cursor = Cursor::new(tape);
let mut fleet = fleet_of(&ROSTER, horizon());
let mut fabric = Fabric::new(u64::from_le_bytes(seed), &ROSTER, 7);
let mut evicted: BTreeSet<u32> = BTreeSet::new();
for _ in 0..MAX_OPS {
let Some((opcode, argument)) = cursor.op() else {
break;
};
let members = live(&fleet, &evicted);
let station = members[usize::from(argument) % members.len()];
let at = (usize::from(argument) / 3) % 8;
let to = (usize::from(argument) / 24) % 8;
match opcode % 10 {
0 => {
let _ = act(&mut fabric, &mut fleet, station, |replica, out| {
replica.insert_visible(at, out)
});
}
1 => {
let _ = act(&mut fabric, &mut fleet, station, |replica, out| {
replica.delete_visible(at, out)
});
}
2 => {
if fleet[&station].adopted() {
let _ = act(&mut fabric, &mut fleet, station, |replica, out| {
replica.delete_visible(at, out)
});
} else {
let _ = act(&mut fabric, &mut fleet, station, |replica, out| {
replica.move_visible(at, Some(to), out)
});
}
}
3 => {
let _ = act(&mut fabric, &mut fleet, station, |replica, out| {
replica.try_declare(out)
});
}
4 => {
let steps = usize::from(argument) % 32 + 1;
for _ in 0..steps {
let address = match delivery {
Delivery::Seeded => None,
Delivery::Addressed => cursor.address(),
};
let _ = fabric.step_addressed(&mut fleet, address);
}
}
5 => fabric.sever(&[station]),
6 => {
fabric.heal();
let gone: Vec<u32> = evicted.iter().copied().collect();
fabric.sever(&gone);
}
7 => {
let roster = roster_of(&fleet[&station]);
crash(&mut fleet, &roster, horizon(), station);
resync(&mut fabric, &fleet, station);
}
8 => {
if let Some(candidate) = CANDIDATES
.iter()
.copied()
.find(|candidate| !fleet.contains_key(candidate))
{
let _ = act(&mut fabric, &mut fleet, station, |replica, out| {
replica.open_arrival(&[candidate], out)
});
}
}
_ => {
if members.len() > 2 {
fabric.heal();
let gone: Vec<u32> = evicted.iter().copied().collect();
fabric.sever(&gone);
fabric.drain(&mut fleet);
while install_admitted(&mut fabric, &mut fleet, &evicted) {
fabric.drain(&mut fleet);
}
let current = live(&fleet, &evicted);
let prior_complete = evicted.iter().all(|&prior| {
current
.iter()
.all(|member| fleet[member].attested(prior).is_some())
});
let expected: BTreeSet<u32> = current.iter().copied().collect();
let roster_ready = current.iter().all(|member| {
roster_of(&fleet[member])
.into_iter()
.filter(|station| !evicted.contains(station))
.collect::<BTreeSet<_>>()
== expected
});
let transition_open = current.iter().any(|member| fleet[member].adopted());
let survivors: Vec<u32> = current
.into_iter()
.filter(|&member| member != station)
.collect();
if survivors.len() >= 2 && prior_complete && roster_ready && !transition_open {
for &survivor in &survivors {
let _ = act(&mut fabric, &mut fleet, survivor, |replica, out| {
replica.open_departure(station, out)
})
.expect("the repaired survivor proposes a gap-free departure prefix");
}
let _ = evicted.insert(station);
fabric.sever(&[station]);
}
}
}
}
let _ = install_admitted(&mut fabric, &mut fleet, &evicted);
}
fabric.heal();
let gone: Vec<u32> = evicted.iter().copied().collect();
fabric.sever(&gone);
for _ in 0..8 {
fabric.drain(&mut fleet);
let wedged = live(&fleet, &evicted)
.iter()
.any(|member| fleet[member].epochs().candidates().count() > 0);
if !evicted.is_empty() || wedged {
for member in live(&fleet, &evicted) {
resync(&mut fabric, &fleet, member);
}
fabric.drain(&mut fleet);
}
let members = live(&fleet, &evicted);
for &station in &evicted {
let unattested = members
.iter()
.any(|member| fleet[member].attested(station).is_none());
if unattested {
for &survivor in &members {
let _ = act(&mut fabric, &mut fleet, survivor, |replica, out| {
replica.open_departure(station, out)
});
}
fabric.drain(&mut fleet);
}
}
let pending = CANDIDATES.iter().copied().find(|&candidate| {
!fleet.contains_key(&candidate)
&& live(&fleet, &evicted).iter().any(|member| {
let held = fleet[member]
.epochs()
.arrival()
.is_some_and(|round| round.admission().admits(candidate));
let admitted = fleet[member]
.epochs()
.sealed()
.filter_map(|sealed| sealed.admission())
.any(|admission| admission.admits(candidate));
held || admitted
})
});
let _ = install_admitted(&mut fabric, &mut fleet, &evicted);
let Some(candidate) = pending else {
let members = live(&fleet, &evicted);
let open_rounds = members
.iter()
.any(|member| fleet[member].epochs().arrival().is_some());
let open_windows = members
.iter()
.any(|member| fleet[member].epochs().candidates().count() > 0);
let unattested = evicted.iter().any(|&gone| {
members
.iter()
.any(|member| fleet[member].attested(gone).is_none())
});
if open_rounds {
settle_boundary(&mut fabric, &mut fleet, &evicted);
}
if !open_rounds && !open_windows && !unattested {
break;
}
continue;
};
for &member in &live(&fleet, &evicted) {
let _ = act(&mut fabric, &mut fleet, member, |replica, out| {
replica.open_arrival(&[candidate], out)
});
}
settle_boundary(&mut fabric, &mut fleet, &evicted);
let _ = install_admitted(&mut fabric, &mut fleet, &evicted);
}
fabric.drain(&mut fleet);
while install_admitted(&mut fabric, &mut fleet, &evicted) {
fabric.drain(&mut fleet);
}
for station in &evicted {
let _ = fleet.remove(station);
}
let survivors: Vec<u32> = fleet.keys().copied().collect();
for &station in &survivors {
assert!(
fleet[&station].epochs().arrival().is_none(),
"station {station}: an arrival round survived the settle --- a wedge",
);
}
assert_converged(&fleet);
assert_quiescent(&fleet);
for &station in &survivors {
let roster: BTreeSet<u32> = fleet[&station].epochs().roster().collect();
for candidate in CANDIDATES {
if fleet.contains_key(&candidate) {
assert!(
roster.contains(&candidate),
"station {station}: installed joiner {candidate} is missing from the roster"
);
}
}
for &gone in &evicted {
assert!(
fleet[&station].attested(gone).is_some(),
"station {station}: eviction of {gone} never attested --- a wedge",
);
}
}
}
fn settle_boundary(
fabric: &mut Fabric,
fleet: &mut BTreeMap<u32, Replica>,
evicted: &BTreeSet<u32>,
) {
let members = live(fleet, evicted);
for &member in &members {
let _ = act(fabric, fleet, member, |replica, out| {
replica.insert_visible(0, out)
});
}
fabric.drain(fleet);
for &member in &members {
let _ = act(fabric, fleet, member, Replica::try_declare);
}
fabric.drain(fleet);
}
pub(in crate::metis) fn run_membership_tape(tape: &[u8]) {
interpret_with(tape, Delivery::Addressed);
}
#[cfg(test)]
#[test]
fn a_directed_admission_and_eviction_survive_the_tape() {
let mut tape = alloc::vec![7u8, 3, 9, 1, 4, 200];
tape.extend_from_slice(&[0, 0, 0, 1, 0, 2, 4, 255, 3, 0, 4, 255, 4, 255]);
tape.extend_from_slice(&[8, 0, 8, 1, 8, 2, 4, 255, 3, 0, 4, 255, 4, 255]);
tape.extend_from_slice(&[9, 2, 4, 255, 3, 0, 4, 255, 4, 255]);
interpret_with(&tape, Delivery::Seeded);
}
#[cfg(test)]
proptest! {
#![proptest_config(ProptestConfig::with_cases(48))]
#[test]
fn any_membership_tape_heals_drains_and_converges(
tape in prop::collection::vec(any::<u8>(), 0..96),
) {
interpret_with(&tape, Delivery::Seeded);
}
#[test]
fn any_addressed_membership_tape_heals_drains_and_converges(
tape in prop::collection::vec(any::<u8>(), 0..96),
) {
run_membership_tape(&tape);
}
}