extern crate alloc;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::collections::BTreeSet;
use alloc::vec::Vec;
use crate::kairos::Kairos;
use crate::metis::{
Admission, Cut, Declaration, Dot, DotSet, Dotted, EpochAddress, Metatheses, Rhapsody,
};
use super::Replica;
pub struct Schedule(u64);
impl Schedule {
pub const fn new(seed: u64) -> Self {
Self(seed ^ 0x9E37_79B9_7F4A_7C15)
}
pub fn next(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
pub fn below(&mut self, bound: usize) -> usize {
let bound = u64::try_from(bound).expect("test-scale bound");
usize::try_from(self.next() % bound).expect("below a usize bound")
}
pub fn one_in(&mut self, out_of: u64) -> bool {
self.next().is_multiple_of(out_of)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OldDelta {
Text(Box<Dotted<Rhapsody>>),
Moves(Dotted<Metatheses>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Note {
Old {
generation: u64,
dots: Vec<Dot>,
delta: OldDelta,
},
Native {
epoch: EpochAddress,
stamp: Kairos,
dots: Vec<Dot>,
delta: OldDelta,
},
Report {
generation: u64,
station: u32,
cut: Cut,
},
Declare {
declaration: Declaration,
},
Confirm {
generation: u64,
epoch: EpochAddress,
station: u32,
cut: Cut,
},
Adoption {
generation: u64,
epoch: EpochAddress,
station: u32,
counter: u64,
},
Retire {
generation: u64,
station: u32,
applied: DotSet,
at: Cut,
},
Depart {
generation: u64,
departing: u32,
by: u32,
prefix: u64,
},
Endorse {
by: u32,
admission: Admission,
commitment: [u8; 32],
},
}
struct Envelope {
src: u32,
dst: u32,
replayable: bool,
note: Note,
}
pub trait Voice {
fn speak(&mut self, dst: u32, note: &Note) -> Utterance;
}
pub enum Utterance {
Truth,
Instead(Note),
Silence,
}
pub struct Withholding {
pub from: u32,
pub kind: fn(&Note) -> bool,
}
impl Voice for Withholding {
fn speak(&mut self, dst: u32, note: &Note) -> Utterance {
if dst == self.from && (self.kind)(note) {
Utterance::Silence
} else {
Utterance::Truth
}
}
}
pub struct Replaying {
pub kind: fn(&Note) -> bool,
pub held: Option<Note>,
}
impl Replaying {
#[must_use]
pub const fn of(kind: fn(&Note) -> bool) -> Self {
Self { kind, held: None }
}
}
impl Voice for Replaying {
fn speak(&mut self, _dst: u32, note: &Note) -> Utterance {
if !(self.kind)(note) {
return Utterance::Truth;
}
match &self.held {
None => {
self.held = Some(note.clone());
Utterance::Truth
}
Some(stale) => Utterance::Instead(stale.clone()),
}
}
}
pub struct Then<A, B>(pub A, pub B);
impl<A: Voice, B: Voice> Voice for Then<A, B> {
fn speak(&mut self, dst: u32, note: &Note) -> Utterance {
match self.0.speak(dst, note) {
Utterance::Silence => Utterance::Silence,
Utterance::Truth => self.1.speak(dst, note),
Utterance::Instead(spoken) => match self.1.speak(dst, &spoken) {
Utterance::Truth => Utterance::Instead(spoken),
other => other,
},
}
}
}
pub struct ForkedAdoption {
pub to: u32,
pub delta: i64,
}
impl Voice for ForkedAdoption {
fn speak(&mut self, dst: u32, note: &Note) -> Utterance {
match note {
Note::Adoption {
generation,
epoch,
station,
counter,
} if dst == self.to => Utterance::Instead(Note::Adoption {
generation: *generation,
epoch: *epoch,
station: *station,
counter: counter.saturating_add_signed(self.delta),
}),
_ => Utterance::Truth,
}
}
}
pub struct ForkedCut {
pub to: u32,
pub kind: fn(&Note) -> bool,
pub at: u32,
pub delta: i64,
}
impl ForkedCut {
fn shift(&self, cut: &Cut) -> Cut {
let mut shifted = crate::metis::VersionVector::new();
for (station, counter) in cut.as_vector() {
let counter = if station == self.at {
counter.saturating_add_signed(self.delta)
} else {
counter
};
if counter > 0 {
shifted.observe(station, counter);
}
}
if self.delta > 0 {
shifted.observe(
self.at,
cut.as_vector()
.get(self.at)
.saturating_add_signed(self.delta),
);
}
Cut::from_witnessed(shifted)
}
}
impl Voice for ForkedCut {
fn speak(&mut self, dst: u32, note: &Note) -> Utterance {
if dst != self.to || !(self.kind)(note) {
return Utterance::Truth;
}
match note {
Note::Report {
generation,
station,
cut,
} => Utterance::Instead(Note::Report {
generation: *generation,
station: *station,
cut: self.shift(cut),
}),
Note::Confirm {
generation,
epoch,
station,
cut,
} => Utterance::Instead(Note::Confirm {
generation: *generation,
epoch: *epoch,
station: *station,
cut: self.shift(cut),
}),
_ => Utterance::Truth,
}
}
}
pub struct Equivocator {
voices: BTreeMap<u32, Box<dyn Voice>>,
budget: usize,
}
impl Equivocator {
pub const fn within(budget: usize) -> Self {
Self {
voices: BTreeMap::new(),
budget,
}
}
#[must_use]
pub fn speaking(mut self, station: u32, voice: Box<dyn Voice>) -> Self {
assert!(
self.voices.len() < self.budget,
"the fault set may not exceed its declared budget of {}",
self.budget
);
assert!(
self.voices.insert(station, voice).is_none(),
"station {station} is already seated"
);
self
}
fn speak(&mut self, src: u32, dst: u32, note: &Note) -> Utterance {
self.voices
.get_mut(&src)
.map_or(Utterance::Truth, |voice| voice.speak(dst, note))
}
}
pub struct Fabric {
schedule: Schedule,
roster: Vec<u32>,
pool: Vec<Envelope>,
severed: BTreeSet<u32>,
duplicate_one_in: u64,
equivocator: Option<Equivocator>,
}
impl Fabric {
pub fn new(seed: u64, roster: &[u32], duplicate_one_in: u64) -> Self {
Self {
schedule: Schedule::new(seed),
roster: roster.to_vec(),
pool: Vec::new(),
severed: BTreeSet::new(),
duplicate_one_in,
equivocator: None,
}
}
pub fn corrupt(&mut self, equivocator: Equivocator) {
self.equivocator = Some(equivocator);
}
pub fn schedule(&mut self) -> &mut Schedule {
&mut self.schedule
}
pub fn broadcast(&mut self, src: u32, note: &Note) {
for &dst in &self.roster {
if dst != src {
let spoken = match self
.equivocator
.as_mut()
.map_or(Utterance::Truth, |seated| seated.speak(src, dst, note))
{
Utterance::Truth => note.clone(),
Utterance::Instead(spoken) => spoken,
Utterance::Silence => continue,
};
self.pool.push(Envelope {
src,
dst,
replayable: true,
note: spoken,
});
}
}
}
pub fn post(&mut self, src: u32, outbox: Vec<Note>) {
for note in outbox {
self.broadcast(src, ¬e);
}
}
pub fn send(&mut self, src: u32, dst: u32, note: &Note) {
let spoken = match self
.equivocator
.as_mut()
.map_or(Utterance::Truth, |seated| seated.speak(src, dst, note))
{
Utterance::Truth => note.clone(),
Utterance::Instead(spoken) => spoken,
Utterance::Silence => return,
};
self.pool.push(Envelope {
src,
dst,
replayable: true,
note: spoken,
});
}
pub fn admit(&mut self, station: u32) {
assert!(
!self.roster.contains(&station),
"station {station} is already on the fabric's roster"
);
self.roster.push(station);
}
pub fn sever(&mut self, stations: &[u32]) {
self.severed.extend(stations.iter().copied());
}
pub fn heal(&mut self) {
self.severed.clear();
}
pub fn in_flight(&self) -> usize {
self.pool.len()
}
pub fn step(&mut self, fleet: &mut BTreeMap<u32, Replica>) -> bool {
self.step_addressed(fleet, None)
}
pub fn step_addressed(
&mut self,
fleet: &mut BTreeMap<u32, Replica>,
address: Option<u8>,
) -> bool {
let deliverable: Vec<usize> = self
.pool
.iter()
.enumerate()
.filter(|(_, envelope)| {
!self.severed.contains(&envelope.src) && !self.severed.contains(&envelope.dst)
})
.map(|(index, _)| index)
.collect();
if deliverable.is_empty() {
return false;
}
let index = address.map_or_else(
|| self.schedule.below(deliverable.len()),
|byte| usize::from(byte) % deliverable.len(),
);
let chosen = deliverable[index];
let envelope = self.pool.swap_remove(chosen);
if envelope.replayable
&& self.duplicate_one_in > 0
&& self.schedule.one_in(self.duplicate_one_in)
{
self.pool.push(Envelope {
src: envelope.src,
dst: envelope.dst,
replayable: false,
note: envelope.note.clone(),
});
}
let replica = fleet
.get_mut(&envelope.dst)
.expect("every envelope addresses a roster member");
let mut outbox = Vec::new();
replica.handle(&envelope.note, &mut outbox);
self.post(envelope.dst, outbox);
true
}
pub fn step_selected(
&mut self,
fleet: &mut BTreeMap<u32, Replica>,
mut wanted: impl FnMut(u32, &Note) -> bool,
) -> bool {
let Some(chosen) = self.pool.iter().position(|envelope| {
!self.severed.contains(&envelope.src)
&& !self.severed.contains(&envelope.dst)
&& wanted(envelope.dst, &envelope.note)
}) else {
return false;
};
let envelope = self.pool.swap_remove(chosen);
let replica = fleet
.get_mut(&envelope.dst)
.expect("every envelope addresses a roster member");
let mut outbox = Vec::new();
replica.handle(&envelope.note, &mut outbox);
self.post(envelope.dst, outbox);
true
}
pub fn drain_selected(
&mut self,
fleet: &mut BTreeMap<u32, Replica>,
mut wanted: impl FnMut(u32, &Note) -> bool,
) {
while self.step_selected(fleet, &mut wanted) {}
}
pub fn drain(&mut self, fleet: &mut BTreeMap<u32, Replica>) {
while self.step(fleet) {}
}
}