use ed25519_dalek::SigningKey;
use super::*;
use crate::collection::empty_metadata_handle;
use crate::collection::records::{
CollectionCommit, CollectionDerive, CollectionHandle, CollectionMerge,
};
fn signer(byte: u8) -> SigningKey {
SigningKey::from_bytes(&[byte; 32])
}
fn public(byte: u8) -> Inline<ED25519PublicKey> {
Inline::new(signer(byte).verifying_key().to_bytes())
}
fn hosted(byte: u8) -> CoverageIndex {
CoverageIndex::for_host(public(byte))
}
fn collection(byte: u8) -> CollectionHandle {
Inline::new([byte; 32])
}
fn data(byte: u8) -> CollectionData {
Inline::new([byte; 32])
}
fn commit(key: u8, into: CollectionHandle, payload: CollectionData) -> CollectionRecord {
CollectionRecord::Commit(CollectionCommit::sign(
&signer(key),
into,
payload,
empty_metadata_handle(),
))
}
fn merge(
key: u8,
into: CollectionHandle,
inputs: &[CollectionData],
result: CollectionData,
) -> CollectionRecord {
CollectionRecord::Merge(
CollectionMerge::sign(&signer(key), into, inputs.iter().copied(), result)
.expect("fixture merges join two to sixteen distinct inputs"),
)
}
fn derive(
key: u8,
target: CollectionHandle,
source: CollectionData,
output: CollectionData,
) -> CollectionRecord {
CollectionRecord::Derive(CollectionDerive::sign(
&signer(key),
target,
SourceLocator::of(source.raw),
output,
))
}
struct Lineages(std::collections::BTreeMap<CollectionHandle, CollectionHandle>);
impl RecordAdmission for Lineages {
fn source(&self, collection: CollectionHandle) -> SourceResolution {
match self.0.get(&collection) {
Some(source) => SourceResolution::Derived(*source),
None => SourceResolution::Root,
}
}
fn admits(
&self,
_collection: CollectionHandle,
_signer: Inline<crate::inline::encodings::ed25519::ED25519PublicKey>,
) -> Admittance {
Admittance::Admitted
}
}
fn projection(hop: usize) -> CollectionHandle {
let mut raw = [0xEEu8; 32];
raw[0] = hop as u8;
Inline::new(raw)
}
fn members(
index: &CoverageIndex,
lineage: CollectionHandle,
node: CollectionData,
) -> Vec<[u8; 32]> {
index
.coverage(lineage, node)
.map(|coverage| coverage.iter_ordered().copied().collect())
.unwrap_or_default()
}
#[test]
fn a_commit_is_its_own_foundation() {
let mut index = CoverageIndex::new();
let c = collection(0);
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
assert_eq!(members(&index, c, data(1)), vec![[1u8; 32]]);
}
#[test]
fn a_node_nobody_attested_has_no_row_at_all() {
let index = CoverageIndex::new();
let c = collection(0);
assert!(index.coverage(c, data(9)).is_none());
assert!(!index.covers(c, data(9), data(9)));
}
#[test]
fn a_merge_covers_every_input() {
let mut index = hosted(1);
let c = collection(0);
for payload in 1..=3 {
index.apply(&commit(1, c, data(payload)), &AdmitEveryRecord);
}
index.apply(
&merge(1, c, &[data(1), data(2), data(3)], data(9)),
&AdmitEveryRecord,
);
assert_eq!(
members(&index, c, data(9)),
vec![[1u8; 32], [2u8; 32], [3u8; 32]]
);
assert_eq!(frontier(&index, c), vec![[9u8; 32]]);
}
#[test]
fn an_n_ary_join_is_all_or_nothing() {
let mut index = hosted(1);
let c = collection(0);
index.apply(
&merge(1, c, &[data(1), data(2), data(3)], data(9)),
&AdmitEveryRecord,
);
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
index.apply(&commit(1, c, data(2)), &AdmitEveryRecord);
assert!(index.coverage(c, data(9)).is_none());
assert!(index.published().has_blocked(c));
assert_eq!(frontier(&index, c), vec![[1u8; 32], [2u8; 32]]);
index.apply(&commit(1, c, data(3)), &AdmitEveryRecord);
assert_eq!(
members(&index, c, data(9)),
vec![[1u8; 32], [2u8; 32], [3u8; 32]]
);
assert!(!index.published().has_blocked(c));
assert_eq!(frontier(&index, c), vec![[9u8; 32]]);
}
#[test]
fn a_leaf_contributes_its_own_output_and_reads_no_row() {
let source = collection(0);
let target = collection(7);
let lineages = Lineages([(target, source)].into_iter().collect());
let mut index = hosted(1);
index.apply(&derive(1, target, data(3), data(4)), &lineages);
assert_eq!(members(&index, target, data(4)), vec![[4u8; 32]]);
assert_eq!(frontier(&index, target), vec![[4u8; 32]]);
assert!(!index.published().has_blocked(target));
assert!(index.coverage(source, data(3)).is_none());
index.apply(&commit(1, source, data(3)), &lineages);
index.apply(&commit(1, source, data(5)), &lineages);
index.apply(&merge(1, source, &[data(3), data(5)], data(6)), &lineages);
assert_eq!(members(&index, target, data(4)), vec![[4u8; 32]]);
assert_eq!(frontier(&index, target), vec![[4u8; 32]]);
}
#[test]
fn records_arriving_before_their_inputs_still_land() {
let c = collection(0);
let mut index = hosted(1);
index.apply(
&merge(1, c, &[data(3), data(4)], data(5)),
&AdmitEveryRecord,
);
index.apply(
&merge(1, c, &[data(1), data(2)], data(3)),
&AdmitEveryRecord,
);
index.apply(&commit(1, c, data(4)), &AdmitEveryRecord);
assert!(index.coverage(c, data(3)).is_none());
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
assert!(index.coverage(c, data(3)).is_none());
index.apply(&commit(1, c, data(2)), &AdmitEveryRecord);
assert_eq!(members(&index, c, data(3)), vec![[1u8; 32], [2u8; 32]]);
assert_eq!(
members(&index, c, data(5)),
vec![[1u8; 32], [2u8; 32], [4u8; 32]]
);
assert_eq!(frontier(&index, c), vec![[5u8; 32]]);
}
#[test]
fn a_row_that_grows_later_widens_everything_downstream_of_it() {
let c = collection(0);
let mut index = hosted(1);
for payload in [1, 2, 6] {
index.apply(&commit(1, c, data(payload)), &AdmitEveryRecord);
}
index.apply(
&merge(1, c, &[data(1), data(2)], data(3)),
&AdmitEveryRecord,
);
index.apply(
&merge(1, c, &[data(3), data(6)], data(7)),
&AdmitEveryRecord,
);
assert_eq!(
members(&index, c, data(7)),
vec![[1u8; 32], [2u8; 32], [6u8; 32]]
);
index.apply(&commit(1, c, data(5)), &AdmitEveryRecord);
index.apply(
&merge(1, c, &[data(1), data(5)], data(3)),
&AdmitEveryRecord,
);
assert_eq!(
members(&index, c, data(7)),
vec![[1u8; 32], [2u8; 32], [5u8; 32], [6u8; 32]]
);
}
#[test]
fn folding_the_same_record_twice_changes_nothing() {
let mut index = hosted(1);
let c = collection(0);
let records = [
commit(1, c, data(1)),
commit(1, c, data(2)),
merge(1, c, &[data(1), data(2)], data(3)),
];
for record in &records {
index.apply(record, &AdmitEveryRecord);
}
let once = members(&index, c, data(3));
for record in &records {
index.apply(record, &AdmitEveryRecord);
}
assert_eq!(members(&index, c, data(3)), once);
assert_eq!(index.len(), 3);
}
#[test]
fn a_cycle_between_a_node_and_its_own_merge_terminates() {
let mut index = hosted(1);
let c = collection(0);
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
index.apply(&commit(1, c, data(2)), &AdmitEveryRecord);
index.apply(
&merge(1, c, &[data(1), data(2)], data(1)),
&AdmitEveryRecord,
);
assert_eq!(members(&index, c, data(1)), vec![[1u8; 32], [2u8; 32]]);
}
struct OnlySigner(u8);
impl RecordAdmission for OnlySigner {
fn source(&self, _collection: CollectionHandle) -> SourceResolution {
SourceResolution::Root
}
fn admits(
&self,
_collection: CollectionHandle,
candidate: Inline<crate::inline::encodings::ed25519::ED25519PublicKey>,
) -> Admittance {
if candidate.raw == signer(self.0).verifying_key().to_bytes() {
Admittance::Admitted
} else {
Admittance::Pending
}
}
}
#[test]
fn a_record_whose_signer_is_not_admitted_yet_is_parked_not_dropped() {
let mut index = hosted(2);
let c = collection(0);
index.apply(&commit(1, c, data(1)), &OnlySigner(1));
index.apply(&commit(2, c, data(2)), &OnlySigner(1));
index.apply(&merge(2, c, &[data(1), data(2)], data(3)), &OnlySigner(1));
assert_eq!(members(&index, c, data(1)), vec![[1u8; 32]]);
assert!(index.coverage(c, data(2)).is_none());
assert!(index.coverage(c, data(3)).is_none());
assert_eq!(index.parked_on_signers(), 1);
assert!(index.published().has_blocked(c));
index.resolve(&AdmitEveryRecord);
assert_eq!(index.parked_on_signers(), 0);
assert_eq!(members(&index, c, data(2)), vec![[2u8; 32]]);
assert_eq!(members(&index, c, data(3)), vec![[1u8; 32], [2u8; 32]]);
assert!(!index.published().has_blocked(c));
}
#[test]
fn unadmitted_is_attributed_to_the_collection_that_holds_it() {
let mut index = CoverageIndex::new();
let a = collection(0);
let b = collection(1);
let untouched = collection(2);
index.apply(&commit(2, a, data(1)), &OnlySigner(1));
index.apply(&commit(2, a, data(2)), &OnlySigner(1));
index.apply(&commit(2, b, data(3)), &OnlySigner(1));
assert_eq!(index.parked_on_signers(), 3, "the aggregate is the sum");
assert_eq!(index.unadmitted_in(a), 2);
assert_eq!(index.unadmitted_in(b), 1);
assert_eq!(index.unadmitted_in(untouched), 0);
index.resolve(&AdmitEveryRecord);
assert_eq!(index.parked_on_signers(), 0);
assert_eq!(index.unadmitted_in(a), 0);
assert_eq!(index.unadmitted_in(b), 0);
}
#[test]
fn unadmitted_members_are_named_not_merely_counted() {
let mut index = CoverageIndex::new();
let a = collection(0);
let b = collection(1);
let untouched = collection(2);
index.apply(&commit(2, a, data(1)), &OnlySigner(1));
index.apply(&commit(2, a, data(2)), &OnlySigner(1));
index.apply(&commit(2, b, data(3)), &OnlySigner(1));
assert_eq!(
index.unadmitted_nodes_in(a),
BTreeSet::from([data(1), data(2)]),
"the members themselves, not how many of them there are"
);
assert_eq!(index.unadmitted_nodes_in(b), BTreeSet::from([data(3)]));
assert!(index.unadmitted_nodes_in(untouched).is_empty());
index.apply(&commit(3, a, data(1)), &OnlySigner(1));
assert_eq!(index.unadmitted_in(a), 3);
assert_eq!(
index.unadmitted_nodes_in(a),
BTreeSet::from([data(1), data(2)])
);
index.resolve(&AdmitEveryRecord);
assert!(index.unadmitted_nodes_in(a).is_empty());
assert!(index.unadmitted_nodes_in(b).is_empty());
}
#[test]
fn a_foreign_route_contributes_nothing_to_a_node_a_host_route_reached() {
let mut index = hosted(1);
let c = collection(0);
for payload in [1, 2, 5] {
index.apply(&commit(1, c, data(payload)), &AdmitEveryRecord);
}
index.apply(
&merge(1, c, &[data(1), data(2)], data(3)),
&AdmitEveryRecord,
);
index.apply(
&merge(2, c, &[data(1), data(5)], data(3)),
&AdmitEveryRecord,
);
assert_eq!(members(&index, c, data(3)), vec![[1u8; 32], [2u8; 32]]);
assert_eq!(frontier(&index, c), vec![[3u8; 32], [5u8; 32]]);
assert_eq!(index.stored_joins(), 1);
assert_eq!(index.parked(), 0);
}
#[test]
fn only_a_leaf_parks_on_its_unresolvable_descriptor() {
struct NoDescriptor;
impl RecordAdmission for NoDescriptor {
fn source(&self, collection: CollectionHandle) -> SourceResolution {
SourceResolution::Missing(collection)
}
fn admits(
&self,
_collection: CollectionHandle,
_signer: Inline<crate::inline::encodings::ed25519::ED25519PublicKey>,
) -> Admittance {
Admittance::Admitted
}
}
let source = collection(0);
let target = collection(7);
let mut index = hosted(1);
index.apply(&commit(1, source, data(1)), &NoDescriptor);
index.apply(&commit(1, source, data(2)), &NoDescriptor);
index.apply(
&merge(1, source, &[data(1), data(2)], data(3)),
&NoDescriptor,
);
index.apply(&derive(1, target, data(3), data(4)), &NoDescriptor);
assert_eq!(members(&index, source, data(3)), vec![[1u8; 32], [2u8; 32]]);
assert!(index.coverage(target, data(4)).is_none());
assert_eq!(index.parked_on_lineages(), 1);
assert_eq!(index.parked_on_signers(), 0);
index.settle(
&Lineages([(target, source)].into_iter().collect()),
[target],
false,
);
assert_eq!(members(&index, target, data(4)), vec![[4u8; 32]]);
assert_eq!(index.parked(), 0);
}
#[test]
fn settling_without_the_awaited_arrival_does_nothing() {
let source = collection(0);
let mut index = CoverageIndex::new();
index.apply(&commit(1, source, data(1)), &OnlySigner(1));
index.apply(&commit(2, source, data(2)), &OnlySigner(1));
assert_eq!(index.parked_on_signers(), 1);
index.settle(&OnlySigner(1), [], false);
assert_eq!(index.parked_on_signers(), 1);
assert!(index.coverage(source, data(2)).is_none());
index.settle(&AdmitEveryRecord, [], true);
assert_eq!(index.parked(), 0);
assert_eq!(members(&index, source, data(2)), vec![[2u8; 32]]);
}
#[test]
fn set_algebra_between_two_nodes_is_patch_algebra() {
let mut index = hosted(1);
let c = collection(0);
for byte in 1..=3u8 {
index.apply(&commit(1, c, data(byte)), &AdmitEveryRecord);
}
index.apply(
&merge(1, c, &[data(1), data(2)], data(4)),
&AdmitEveryRecord,
);
index.apply(
&merge(1, c, &[data(2), data(3)], data(5)),
&AdmitEveryRecord,
);
let left = index.coverage(c, data(4)).expect("left row").clone();
let right = index.coverage(c, data(5)).expect("right row").clone();
let shared: Vec<_> = left.intersect(&right).iter_ordered().copied().collect();
assert_eq!(shared, vec![[2u8; 32]]);
let only_left: Vec<_> = left.difference(&right).iter_ordered().copied().collect();
assert_eq!(only_left, vec![[1u8; 32]]);
}
#[test]
fn one_payload_in_two_lattices_keeps_two_rows() {
let mut index = hosted(1);
let ledger = collection(0);
let tally = collection(1);
let shared = data(3);
index.apply(&commit(1, ledger, shared), &AdmitEveryRecord);
index.apply(&commit(1, tally, data(1)), &AdmitEveryRecord);
index.apply(&commit(1, tally, data(2)), &AdmitEveryRecord);
index.apply(
&merge(1, tally, &[data(1), data(2)], shared),
&AdmitEveryRecord,
);
assert_eq!(members(&index, ledger, shared), vec![[3u8; 32]]);
assert_eq!(members(&index, tally, shared), vec![[1u8; 32], [2u8; 32]]);
}
#[test]
fn a_foreign_member_cannot_flip_a_subsumption_verdict() {
let tally = collection(0);
let ledger = collection(1);
let alias = data(3);
let mut index = hosted(1);
for payload in [1, 2, 4] {
index.apply(&commit(1, tally, data(payload)), &AdmitEveryRecord);
}
index.apply(
&merge(1, tally, &[data(1), data(4)], alias),
&AdmitEveryRecord,
);
index.apply(
&merge(1, tally, &[data(1), data(2)], data(5)),
&AdmitEveryRecord,
);
index.apply(&commit(1, ledger, data(2)), &AdmitEveryRecord);
index.apply(&commit(1, ledger, data(6)), &AdmitEveryRecord);
index.apply(
&merge(1, ledger, &[data(2), data(6)], alias),
&AdmitEveryRecord,
);
let alias_row = index.coverage(tally, alias).expect("row").clone();
let pair = index.coverage(tally, data(5)).expect("row").clone();
assert_eq!(
alias_row.iter_ordered().copied().collect::<Vec<_>>(),
vec![[1u8; 32], [4u8; 32]]
);
assert!(
!pair.difference(&alias_row).is_empty(),
"a node tally still needs must not read as redundant"
);
assert_eq!(members(&index, ledger, alias), vec![[2u8; 32], [6u8; 32]]);
}
#[test]
fn owners_name_the_signer_of_every_believed_foundation_and_no_join() {
let c = collection(0);
let mut index = hosted(1);
index.apply(&commit(1, c, data(1)), &OnlySigner(1));
index.apply(&commit(2, c, data(2)), &AdmitEveryRecord);
index.apply(&commit(1, c, data(4)), &OnlySigner(1));
index.apply(&merge(1, c, &[data(1), data(2)], data(5)), &OnlySigner(1));
index.apply(&commit(2, c, data(8)), &AdmitEveryRecord);
index.apply(&merge(1, c, &[data(4), data(8)], data(8)), &OnlySigner(1));
index.apply(
&merge(3, c, &[data(1), data(4)], data(7)),
&AdmitEveryRecord,
);
index.apply(&commit(4, c, data(6)), &OnlySigner(1));
let published = index.published();
assert_eq!(published.owners(c, data(1)), vec![public(1)]);
assert_eq!(published.owners(c, data(2)), vec![public(2)]);
assert!(!published.owned_by(c, data(2), public(1)));
assert_eq!(published.owners(c, data(8)), vec![public(2)]);
assert!(!published.owned_by(c, data(8), public(1)));
assert_eq!(members(&index, c, data(8)), vec![[4u8; 32], [8u8; 32]]);
assert!(published.owners(c, data(5)).is_empty());
assert_eq!(
members(&index, c, data(5)),
vec![[1u8; 32], [2u8; 32]],
"control: the host's join was driven"
);
assert!(published.owners(c, data(7)).is_empty());
assert!(index.coverage(c, data(7)).is_none());
assert!(published.owners(c, data(6)).is_empty());
assert!(published.owners(collection(9), data(1)).is_empty());
index.resolve(&AdmitEveryRecord);
assert_eq!(index.published().owners(c, data(6)), vec![public(4)]);
}
#[test]
fn leaves_are_published_by_source_locator() {
let source = collection(0);
let target = collection(7);
let lineages = Lineages([(target, source)].into_iter().collect());
let mut index = CoverageIndex::new();
index.apply(&derive(1, target, data(1), data(11)), &lineages);
index.apply(&derive(2, target, data(2), data(12)), &lineages);
index.apply(&derive(1, source, data(3), data(13)), &lineages);
let published = index.published();
let first = SourceLocator::of(data(1).raw);
let second = SourceLocator::of(data(2).raw);
assert!(published.has_leaf(target, first));
assert_eq!(published.leaf_outputs(target, first), vec![data(11)]);
assert_eq!(published.leaf_outputs(target, second), vec![data(12)]);
assert!(!published.has_leaf(target, SourceLocator::of(data(3).raw)));
assert!(!published.has_leaf(source, SourceLocator::of(data(3).raw)));
assert!(!published.has_leaf(target, SourceLocator::from_raw(data(1).raw)));
let mut expected = vec![(first, data(11)), (second, data(12))];
expected.sort();
assert_eq!(published.leaves(target), expected);
assert!(published.leaves(source).is_empty());
assert_eq!(published.owners(target, data(12)), vec![public(2)]);
}
#[test]
fn a_derive_into_a_root_attests_nothing() {
let root = collection(0);
let lineages = Lineages(std::collections::BTreeMap::new());
let mut index = CoverageIndex::new();
index.apply(&derive(1, root, data(3), data(4)), &lineages);
assert!(index.coverage(root, data(4)).is_none());
assert!(frontier(&index, root).is_empty());
assert!(index.published().owners(root, data(4)).is_empty());
assert_eq!(index.parked(), 0);
}
#[test]
#[ignore = "timing probe; run with --ignored --nocapture"]
fn folding_a_realistic_lattice_is_cheap_enough_to_do_on_open() {
fn probe(commits: usize, derive_hops: usize) {
let source = collection(0);
let mut records = Vec::new();
let mut tier: Vec<CollectionData> = Vec::with_capacity(commits);
for index in 0..commits {
let mut raw = [0u8; 32];
raw[..8].copy_from_slice(&(index as u64).to_be_bytes());
let payload = Inline::new(raw);
records.push(commit(1, source, payload));
tier.push(payload);
}
let mut next_result = commits as u64;
while tier.len() > 1 {
let mut above = Vec::with_capacity(tier.len().div_ceil(8));
for group in tier.chunks(8) {
if group.len() >= 2 {
let mut raw = [0u8; 32];
raw[..8].copy_from_slice(&next_result.to_be_bytes());
next_result += 1;
let result = Inline::new(raw);
records.push(merge(1, source, group, result));
above.push(result);
} else {
above.push(group[0]);
}
}
tier = above;
}
let root = tier[0];
for hop in 0..derive_hops {
let mut raw = [0u8; 32];
raw[..8].copy_from_slice(&next_result.to_be_bytes());
next_result += 1;
records.push(derive(1, projection(hop), root, Inline::new(raw)));
}
let lineages = Lineages(
(0..derive_hops)
.map(|hop| {
let from = if hop == 0 {
source
} else {
projection(hop - 1)
};
(projection(hop), from)
})
.collect(),
);
let started = std::time::Instant::now();
let mut index = hosted(1);
for record in &records {
index.apply(record, &lineages);
}
let elapsed = started.elapsed();
assert_eq!(
index.coverage(source, root).map(|row| row.len()),
Some(commits as u64)
);
println!(
"{:>7} records ({commits} commits, {derive_hops} derive hops): \
{:>8.2?} total, {:>6.2} us/record",
records.len(),
elapsed,
elapsed.as_secs_f64() * 1e6 / records.len() as f64,
);
}
probe(410, 2);
probe(1_722, 2);
probe(14_980, 2);
probe(100_000, 2);
}
fn frontier(index: &CoverageIndex, collection: CollectionHandle) -> Vec<[u8; 32]> {
index
.published()
.frontier(collection)
.map(|node| node.raw)
.collect()
}
#[test]
fn the_frontier_is_the_set_of_unmerged_nodes() {
let mut index = hosted(1);
let c = collection(0);
for payload in 1..=4 {
index.apply(&commit(1, c, data(payload)), &AdmitEveryRecord);
}
assert_eq!(
frontier(&index, c),
vec![[1u8; 32], [2u8; 32], [3u8; 32], [4u8; 32]]
);
index.apply(
&merge(1, c, &[data(1), data(2)], data(5)),
&AdmitEveryRecord,
);
index.apply(
&merge(1, c, &[data(3), data(4)], data(6)),
&AdmitEveryRecord,
);
assert_eq!(frontier(&index, c), vec![[5u8; 32], [6u8; 32]]);
index.apply(
&merge(1, c, &[data(5), data(6)], data(7)),
&AdmitEveryRecord,
);
assert_eq!(frontier(&index, c), vec![[7u8; 32]]);
let (support, unattested) = index.published().frontier_support(c);
assert!(unattested.is_empty());
assert_eq!(
support.iter_ordered().copied().collect::<Vec<_>>(),
vec![[1u8; 32], [2u8; 32], [3u8; 32], [4u8; 32]]
);
}
#[test]
fn a_merge_arriving_before_its_inputs_settles_on_the_same_frontier() {
let mut index = hosted(1);
let c = collection(0);
index.apply(
&merge(1, c, &[data(1), data(2)], data(5)),
&AdmitEveryRecord,
);
assert!(frontier(&index, c).is_empty());
assert!(index.published().has_blocked(c));
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
assert_eq!(frontier(&index, c), vec![[1u8; 32]]);
assert!(index.published().has_blocked(c));
index.apply(&commit(1, c, data(2)), &AdmitEveryRecord);
assert_eq!(frontier(&index, c), vec![[5u8; 32]]);
assert!(!index.published().has_blocked(c));
}
#[test]
fn blocked_joins_do_not_depend_on_arrival_order() {
let c = collection(0);
let driven = merge(1, c, &[data(1), data(2)], data(20));
let stuck = merge(1, c, &[data(3), data(9)], data(20));
let foundations = [
commit(1, c, data(1)),
commit(1, c, data(2)),
commit(1, c, data(3)),
];
let fold = |records: &[&CollectionRecord]| {
let mut index = hosted(1);
for record in records {
index.apply(record, &AdmitEveryRecord);
}
index
};
let [a, b, d] = &foundations;
let driven_first = fold(&[a, b, d, &driven, &stuck]);
let stuck_first = fold(&[a, b, d, &stuck, &driven]);
assert!(driven_first.published().has_blocked(c));
assert!(stuck_first.published().has_blocked(c));
assert_eq!(driven_first.published(), stuck_first.published());
let in_order = fold(&[a, b, &driven]);
let reversed = fold(&[&driven, b, a]);
assert!(!in_order.published().has_blocked(c));
assert!(!reversed.published().has_blocked(c));
assert_eq!(in_order.published(), reversed.published());
}
#[test]
fn a_commit_arriving_after_the_merge_that_consumes_it_is_not_put_back() {
let mut index = hosted(1);
let c = collection(0);
index.apply(&commit(1, c, data(2)), &AdmitEveryRecord);
index.apply(&commit(1, c, data(3)), &AdmitEveryRecord);
index.apply(
&merge(1, c, &[data(1), data(2), data(3)], data(5)),
&AdmitEveryRecord,
);
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
assert_eq!(frontier(&index, c), vec![[5u8; 32]]);
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
index.apply(
&merge(1, c, &[data(1), data(2), data(3)], data(5)),
&AdmitEveryRecord,
);
assert_eq!(frontier(&index, c), vec![[5u8; 32]]);
}
#[test]
fn two_routes_to_one_result_consume_both_routes() {
let mut index = hosted(1);
let c = collection(0);
for payload in 1..=4 {
index.apply(&commit(1, c, data(payload)), &AdmitEveryRecord);
}
index.apply(
&merge(1, c, &[data(1), data(2)], data(5)),
&AdmitEveryRecord,
);
index.apply(
&merge(1, c, &[data(3), data(4)], data(5)),
&AdmitEveryRecord,
);
assert_eq!(frontier(&index, c), vec![[5u8; 32]]);
assert_eq!(
members(&index, c, data(5)),
vec![[1u8; 32], [2u8; 32], [3u8; 32], [4u8; 32]]
);
}
#[test]
fn absorption_keeps_the_absorbing_node_on_the_frontier() {
let mut index = hosted(1);
let c = collection(0);
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
index.apply(&commit(1, c, data(2)), &AdmitEveryRecord);
index.apply(
&merge(1, c, &[data(1), data(2)], data(2)),
&AdmitEveryRecord,
);
assert_eq!(frontier(&index, c), vec![[2u8; 32]]);
assert_eq!(members(&index, c, data(2)), vec![[1u8; 32], [2u8; 32]]);
index.apply(&commit(1, c, data(1)), &AdmitEveryRecord);
index.apply(
&merge(1, c, &[data(1), data(2)], data(2)),
&AdmitEveryRecord,
);
assert_eq!(frontier(&index, c), vec![[2u8; 32]]);
}
#[test]
fn leaves_join_the_target_frontier_and_a_target_merge_consumes_them() {
let source = collection(0);
let target = collection(7);
let lineages = Lineages([(target, source)].into_iter().collect());
let mut index = hosted(1);
index.apply(&commit(1, source, data(1)), &lineages);
index.apply(&commit(1, source, data(2)), &lineages);
index.apply(&derive(1, target, data(1), data(11)), &lineages);
index.apply(&derive(1, target, data(2), data(12)), &lineages);
assert_eq!(frontier(&index, target), vec![[11u8; 32], [12u8; 32]]);
index.apply(&derive(1, target, data(9), data(19)), &lineages);
assert!(!index.published().has_blocked(target));
assert_eq!(
frontier(&index, target),
vec![[11u8; 32], [12u8; 32], [19u8; 32]]
);
assert_eq!(frontier(&index, source), vec![[1u8; 32], [2u8; 32]]);
index.apply(
&merge(1, target, &[data(11), data(12), data(19)], data(13)),
&lineages,
);
assert_eq!(frontier(&index, target), vec![[13u8; 32]]);
assert_eq!(
members(&index, target, data(13)),
vec![[11u8; 32], [12u8; 32], [19u8; 32]]
);
index.apply(&merge(1, source, &[data(1), data(2)], data(3)), &lineages);
assert_eq!(frontier(&index, source), vec![[3u8; 32]]);
assert_eq!(frontier(&index, target), vec![[13u8; 32]]);
}
#[test]
fn a_commit_written_into_a_derived_collection_attests_nothing() {
let source = collection(0);
let target = collection(7);
let lineages = Lineages([(target, source)].into_iter().collect());
let mut index = CoverageIndex::new();
index.apply(&commit(1, target, data(9)), &lineages);
assert!(index.coverage(target, data(9)).is_none());
assert!(frontier(&index, target).is_empty());
assert!(index.published().owners(target, data(9)).is_empty());
assert_eq!(index.parked(), 0);
}
struct AdmitOnly(Inline<ED25519PublicKey>);
impl RecordAdmission for AdmitOnly {
fn source(&self, _collection: CollectionHandle) -> SourceResolution {
SourceResolution::Root
}
fn admits(
&self,
_collection: CollectionHandle,
signer: Inline<ED25519PublicKey>,
) -> Admittance {
if signer == self.0 {
Admittance::Admitted
} else {
Admittance::Pending
}
}
}
#[test]
fn a_join_keeps_its_inputs_once_however_many_edges_and_fresh_copies_name_it() {
let c = collection(1);
let inputs: Vec<CollectionData> = (10..18).map(data).collect();
let result = data(40);
let admission = AdmitOnly(public(7));
let mut index = hosted(7);
for input in &inputs {
index.apply(&commit(7, c, *input), &admission);
}
let join = merge(7, c, &inputs, result);
index.apply(&join, &admission);
index.apply(&join, &admission);
assert_eq!(index.stored_joins(), 1);
for input in &inputs {
let reading = index.joins_reading(c, *input);
assert_eq!(reading.len(), 1);
assert_eq!(reading[0].0.as_slice(), inputs.as_slice());
assert_eq!(reading[0].1, result);
}
index.apply(&merge(9, c, &inputs, result), &admission);
index.apply(&merge(9, c, &inputs[..4], data(42)), &admission);
assert_eq!(index.parked(), 0);
assert_eq!(index.stored_joins(), 1);
let other = data(41);
let second = merge(7, c, &inputs[..4], other);
index.park_record(&second);
index.park_record(&second);
assert_eq!(index.stored_joins(), 2);
assert!(index.has_fresh());
assert!(index.coverage(c, other).is_none());
index.settle(&AdmitOnly(public(1)), std::iter::empty(), false);
assert_eq!(index.parked(), 0);
assert_eq!(
members(&index, c, other),
inputs[..4]
.iter()
.map(|input| input.raw)
.collect::<Vec<_>>()
);
assert_eq!(index.stored_joins(), 2);
}
#[test]
fn a_parked_leaf_and_a_parked_join_come_back_whole() {
let root = collection(1);
let view = collection(2);
let lineages = Lineages([(view, root)].into_iter().collect());
let mut index = hosted(5);
index.park_record(&derive(5, view, data(10), data(20)));
index.park_record(&derive(5, view, data(11), data(21)));
index.park_record(&merge(5, view, &[data(20), data(21)], data(22)));
assert_eq!(index.stored_joins(), 1);
index.settle(&lineages, std::iter::empty(), false);
assert_eq!(members(&index, view, data(22)), vec![[20; 32], [21; 32]]);
assert!(index
.published()
.has_leaf(view, SourceLocator::of(data(10).raw)));
assert_eq!(
index
.published()
.leaf_outputs(view, SourceLocator::of(data(11).raw)),
vec![data(21)]
);
assert_eq!(
index.published().frontier(view).collect::<Vec<_>>(),
vec![data(22)]
);
}
#[test]
fn a_foreign_merge_leaves_the_index_as_a_fold_without_it() {
let c = collection(0);
let (a, b) = (data(1), data(2));
let own = [
commit(1, c, a),
commit(1, c, b),
commit(1, c, data(3)),
commit(2, c, data(4)),
merge(1, c, &[data(3), data(4)], data(5)),
];
let foreign = merge(2, c, &[a, b], a);
let applied = |records: &[&CollectionRecord]| {
let mut index = hosted(1);
for record in records {
index.apply(record, &AdmitEveryRecord);
}
index
};
let parked = |records: &[&CollectionRecord]| {
let mut index = hosted(1);
for record in records {
index.park_record(record);
}
index.settle(&AdmitEveryRecord, std::iter::empty(), false);
index
};
let without: Vec<&CollectionRecord> = own.iter().collect();
let after: Vec<&CollectionRecord> = own.iter().chain([&foreign]).collect();
let before: Vec<&CollectionRecord> = [&foreign].into_iter().chain(own.iter()).collect();
let reference = applied(&without);
assert_eq!(members(&reference, c, a), vec![[1u8; 32]]);
assert_eq!(
frontier(&reference, c),
vec![[1u8; 32], [2u8; 32], [5u8; 32]]
);
assert_eq!(reference.stored_joins(), 1);
for (name, index) in [
("after its inputs", applied(&after)),
("before its inputs", applied(&before)),
("parked after its inputs", parked(&after)),
("parked before its inputs", parked(&before)),
] {
assert_eq!(index.published(), reference.published(), "{name}");
assert_eq!(index, reference, "{name}");
}
}
#[test]
fn a_host_join_over_a_node_only_a_foreign_merge_produced_stays_blocked() {
let c = collection(0);
let mut index = hosted(1);
for payload in [1, 2, 3] {
index.apply(&commit(1, c, data(payload)), &AdmitEveryRecord);
}
index.apply(
&merge(2, c, &[data(1), data(2)], data(5)),
&AdmitEveryRecord,
);
index.apply(
&merge(1, c, &[data(5), data(3)], data(6)),
&AdmitEveryRecord,
);
assert!(index.coverage(c, data(5)).is_none());
assert!(index.coverage(c, data(6)).is_none());
assert!(index.published().has_blocked(c));
assert!(index.published().producers(c, data(6)).is_empty());
assert!(index.published().producers(c, data(5)).is_empty());
assert_eq!(frontier(&index, c), vec![[1u8; 32], [2u8; 32], [3u8; 32]]);
let blocked = MergeInputs::new([data(3), data(5)]).unwrap();
assert!(!index.published().producers(c, data(6)).contains(&blocked));
assert_eq!(index.joins_reading(c, data(3)), vec![(blocked, data(6))]);
index.apply(
&merge(1, c, &[data(1), data(2)], data(5)),
&AdmitEveryRecord,
);
assert!(!index.published().has_blocked(c));
assert!(index.published().producers(c, data(6)).contains(&blocked));
assert_eq!(index.published().producers(c, data(6)), vec![blocked]);
assert_eq!(
members(&index, c, data(6)),
vec![[1u8; 32], [2u8; 32], [3u8; 32]]
);
assert_eq!(frontier(&index, c), vec![[6u8; 32]]);
}
struct OnlyWriterAndNoDescriptor(Inline<ED25519PublicKey>);
impl RecordAdmission for OnlyWriterAndNoDescriptor {
fn source(&self, collection: CollectionHandle) -> SourceResolution {
SourceResolution::Missing(collection)
}
fn admits(
&self,
_collection: CollectionHandle,
signer: Inline<ED25519PublicKey>,
) -> Admittance {
if signer == self.0 {
Admittance::Admitted
} else {
Admittance::Undescribed
}
}
}
#[test]
fn a_host_join_is_believed_without_write_or_a_descriptor() {
let c = collection(0);
let admission = OnlyWriterAndNoDescriptor(public(1));
let mut index = hosted(9);
index.apply(&commit(1, c, data(1)), &admission);
index.apply(&commit(1, c, data(2)), &admission);
index.apply(&commit(9, c, data(3)), &admission);
index.apply(&merge(9, c, &[data(1), data(2)], data(4)), &admission);
assert_eq!(members(&index, c, data(4)), vec![[1u8; 32], [2u8; 32]]);
assert_eq!(frontier(&index, c), vec![[4u8; 32]]);
assert_eq!(index.parked_on_lineages(), 1);
assert_eq!(index.parked_on_signers(), 0);
assert_eq!(index.unadmitted_nodes_in(c), BTreeSet::new());
assert!(index.coverage(c, data(3)).is_none());
}
#[test]
fn producers_are_the_host_joins_that_drive_a_node() {
let c = collection(0);
let mut index = hosted(1);
for payload in [1, 2, 3, 4] {
index.apply(&commit(1, c, data(payload)), &AdmitEveryRecord);
}
let first = MergeInputs::new([data(1), data(2)]).unwrap();
let second = MergeInputs::new([data(3), data(4)]).unwrap();
let waiting = MergeInputs::new([data(3), data(9)]).unwrap();
let absorbing = MergeInputs::new([data(4), data(7)]).unwrap();
for inputs in [first, second, waiting] {
index.apply(&merge(1, c, inputs.as_slice(), data(7)), &AdmitEveryRecord);
}
index.apply(
&merge(1, c, absorbing.as_slice(), data(7)),
&AdmitEveryRecord,
);
index.apply(
&merge(2, c, &[data(2), data(3)], data(7)),
&AdmitEveryRecord,
);
let mut expected = vec![first, second, absorbing];
let producers = index.published().producers(c, data(7));
assert_eq!(producers.len(), 3);
expected.retain(|inputs| producers.contains(inputs));
assert_eq!(expected.len(), 3);
assert!(!producers.contains(&waiting));
assert!(index.published().producers(c, data(1)).is_empty());
assert!(index
.published()
.producers(collection(5), data(7))
.is_empty());
}
#[test]
fn a_host_join_nobody_has_decided_is_no_producer() {
let (earlier, read) = (collection(1), collection(2));
let mut index = hosted(1);
for payload in [1, 3] {
index.park_record(&commit(1, earlier, data(payload)));
}
index.settle_collections(&AdmitEveryRecord, &BTreeSet::from([earlier]));
let waiting = MergeInputs::new([data(1), data(2)]).unwrap();
let drivable = MergeInputs::new([data(1), data(3)]).unwrap();
index.park_record(&merge(1, earlier, waiting.as_slice(), data(5)));
index.park_record(&merge(1, earlier, drivable.as_slice(), data(6)));
index.park_record(&commit(1, read, data(9)));
index.settle_collections(&AdmitEveryRecord, &BTreeSet::from([read]));
assert!(index.has_fresh());
assert_eq!(index.stored_joins(), 2);
assert!(!index.published().has_blocked(earlier));
assert!(index.published().producers(earlier, data(5)).is_empty());
assert!(index.published().producers(earlier, data(6)).is_empty());
assert!(index.joins_reading(earlier, data(1)).is_empty());
index.settle_collections(&AdmitEveryRecord, &BTreeSet::from([earlier]));
assert!(!index.has_fresh());
assert_eq!(index.stored_joins(), 2);
assert!(index.published().has_blocked(earlier));
assert!(index.published().producers(earlier, data(5)).is_empty());
assert_eq!(
index.published().producers(earlier, data(6)),
vec![drivable]
);
assert!(!index
.published()
.producers(earlier, data(5))
.contains(&waiting));
assert_eq!(
index.joins_reading(earlier, data(2)),
vec![(waiting, data(5))]
);
index.apply(&commit(1, earlier, data(2)), &AdmitEveryRecord);
assert!(index
.published()
.producers(earlier, data(5))
.contains(&waiting));
assert_eq!(index.published().producers(earlier, data(5)), vec![waiting]);
index.park_record(&merge(1, earlier, waiting.as_slice(), data(5)));
assert!(!index.has_fresh());
assert_eq!(index.stored_joins(), 2);
}