use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use ed25519_dalek::VerifyingKey;
use crate::inline::encodings::ed25519::ED25519PublicKey;
use crate::inline::Inline;
use crate::patch::{Blake3Merkle, Entry, IdentitySchema, PATCH};
use crate::repo::{BlobStoreGet, CapabilityProofRead};
use super::api::AdmissionEvidence;
use super::records::{
CollectionData, CollectionHandle, CollectionRecord, MergeInputs, SourceLocator,
};
#[cfg(test)]
use super::store::CollectionRead;
use crate::capability::QuorumOutcome;
pub type CoverageSet = PATCH<32, IdentitySchema, (), Blake3Merkle>;
pub type FrontierSet = PATCH<32, IdentitySchema, ()>;
mod triple_key {
crate::key_segmentation!(Segments, 96, [32, 32, 32]);
crate::key_schema!(Schema, Segments, 96, [0, 1, 2]);
}
type BlockedJoins = PATCH<64, IdentitySchema, ()>;
type OwnerSet = PATCH<96, triple_key::Schema, ()>;
type LeafSet = PATCH<96, triple_key::Schema, ()>;
type AttachmentSet = PATCH<96, triple_key::Schema, ()>;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Coverage {
rows: PATCH<64, IdentitySchema, CoverageSet>,
frontiers: PATCH<32, IdentitySchema, FrontierSet>,
blocked: PATCH<32, IdentitySchema, BlockedJoins>,
owners: OwnerSet,
leaves: LeafSet,
joins: Joins,
attachments: AttachmentSet,
}
fn row_key(collection: CollectionHandle, node: CollectionData) -> [u8; 64] {
let mut key = [0u8; 64];
key[..32].copy_from_slice(&collection.raw);
key[32..].copy_from_slice(&node.raw);
key
}
fn triple(first: [u8; 32], second: [u8; 32], third: [u8; 32]) -> [u8; 96] {
let mut key = [0u8; 96];
key[..32].copy_from_slice(&first);
key[32..64].copy_from_slice(&second);
key[64..].copy_from_slice(&third);
key
}
impl Coverage {
pub fn of(&self, collection: CollectionHandle, node: CollectionData) -> Option<&CoverageSet> {
self.rows.get(&row_key(collection, node))
}
pub fn covers(
&self,
collection: CollectionHandle,
node: CollectionData,
foundation: CollectionData,
) -> bool {
self.of(collection, node)
.is_some_and(|coverage| coverage.get(&foundation.raw).is_some())
}
pub fn union_over(
&self,
collection: CollectionHandle,
nodes: impl IntoIterator<Item = CollectionData>,
) -> (CoverageSet, Vec<CollectionData>) {
let mut union = CoverageSet::new();
let mut unattested = Vec::new();
for node in nodes {
match self.of(collection, node) {
Some(row) => union.union(row.clone()),
None => unattested.push(node),
}
}
(union, unattested)
}
pub fn frontier(
&self,
collection: CollectionHandle,
) -> impl Iterator<Item = CollectionData> + '_ {
self.frontiers
.get(&collection.raw)
.into_iter()
.flat_map(|frontier| frontier.iter_ordered().map(|raw| Inline::new(*raw)))
}
pub fn frontier_set(&self, collection: CollectionHandle) -> Option<&FrontierSet> {
self.frontiers.get(&collection.raw)
}
pub fn frontier_support(
&self,
collection: CollectionHandle,
) -> (CoverageSet, Vec<CollectionData>) {
let nodes: Vec<_> = self.frontier(collection).collect();
self.union_over(collection, nodes)
}
pub fn has_blocked(&self, collection: CollectionHandle) -> bool {
self.blocked
.get(&collection.raw)
.is_some_and(|blocked| !blocked.is_empty())
}
pub fn producers(
&self,
collection: CollectionHandle,
node: CollectionData,
) -> Vec<MergeInputs> {
let prefix = row_key(collection, node);
let mut digests = Vec::new();
self.joins.infixes(&prefix, |digest: &[u8; 32]| {
digests.push(*digest);
});
let blocked = self.blocked.get(&collection.raw);
digests
.into_iter()
.filter(|digest| {
let mut edge = [0u8; 64];
edge[..32].copy_from_slice(&node.raw);
edge[32..].copy_from_slice(digest);
blocked.map_or(true, |blocked| blocked.get(&edge).is_none())
})
.filter_map(|digest| {
self.joins
.get(&triple(collection.raw, node.raw, digest))
.copied()
})
.collect()
}
pub fn owners(
&self,
collection: CollectionHandle,
node: CollectionData,
) -> Vec<Inline<ED25519PublicKey>> {
let mut prefix = [0u8; 64];
prefix[..32].copy_from_slice(&collection.raw);
prefix[32..].copy_from_slice(&node.raw);
let mut owners = Vec::new();
self.owners.infixes(&prefix, |signer: &[u8; 32]| {
owners.push(Inline::new(*signer));
});
owners.sort_unstable_by(|left, right| left.raw.cmp(&right.raw));
owners
}
pub fn owned_by(
&self,
collection: CollectionHandle,
node: CollectionData,
signer: Inline<ED25519PublicKey>,
) -> bool {
self.owners
.get(&triple(collection.raw, node.raw, signer.raw))
.is_some()
}
pub fn has_leaf(&self, collection: CollectionHandle, locator: SourceLocator) -> bool {
let mut prefix = [0u8; 64];
prefix[..32].copy_from_slice(&collection.raw);
prefix[32..].copy_from_slice(locator.as_bytes());
self.leaves.has_prefix(&prefix)
}
pub fn leaf_outputs(
&self,
collection: CollectionHandle,
locator: SourceLocator,
) -> Vec<CollectionData> {
let mut prefix = [0u8; 64];
prefix[..32].copy_from_slice(&collection.raw);
prefix[32..].copy_from_slice(locator.as_bytes());
let mut outputs = Vec::new();
self.leaves.infixes(&prefix, |output: &[u8; 32]| {
outputs.push(Inline::new(*output));
});
outputs.sort_unstable_by(|left, right| left.raw.cmp(&right.raw));
outputs
}
pub fn leaves(&self, collection: CollectionHandle) -> Vec<(SourceLocator, CollectionData)> {
let mut locators = Vec::new();
self.leaves.infixes(&collection.raw, |locator: &[u8; 32]| {
locators.push(SourceLocator::from_raw(*locator));
});
locators.sort_unstable();
let mut leaves = Vec::new();
for locator in locators {
for output in self.leaf_outputs(collection, locator) {
leaves.push((locator, output));
}
}
leaves
}
pub fn attachments(
&self,
attached: CollectionHandle,
node: CollectionData,
) -> Vec<CollectionData> {
let prefix = row_key(attached, node);
let mut attachments = Vec::new();
self.attachments.infixes(&prefix, |attachment: &[u8; 32]| {
attachments.push(Inline::new(*attachment));
});
attachments.sort_unstable_by(|left, right| left.raw.cmp(&right.raw));
attachments
}
pub fn len(&self) -> usize {
self.rows.len().min(usize::MAX as u64) as usize
}
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
pub fn shares_root(&self, other: &Self) -> bool {
self.rows.shares_root(&other.rows)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Driven {
Grew,
Unchanged,
Blocked,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum Attestation {
Foundation { data: CollectionData },
Leaf {
locator: SourceLocator,
output: CollectionData,
},
Join {
inputs: MergeInputs,
result: CollectionData,
},
Attachment {
node: CollectionData,
attachment: CollectionData,
},
}
pub(super) fn produced(record: CollectionRecord) -> CollectionData {
Attestation::of(&record).result()
}
impl Attestation {
pub fn of(record: &CollectionRecord) -> Self {
match record {
CollectionRecord::Commit(commit) => Self::Foundation {
data: commit.data(),
},
CollectionRecord::Merge(merge) => Self::Join {
inputs: merge.merge_inputs(),
result: merge.result(),
},
CollectionRecord::Derive(derive) => Self::Leaf {
locator: derive.input(),
output: derive.output(),
},
CollectionRecord::Map(map) => Self::Attachment {
node: map.node(),
attachment: map.attachment(),
},
}
}
pub fn result(&self) -> CollectionData {
match self {
Self::Foundation { data } => *data,
Self::Leaf { output, .. } => *output,
Self::Join { result, .. } => *result,
Self::Attachment { attachment, .. } => *attachment,
}
}
fn inputs(&self) -> &[CollectionData] {
match self {
Self::Join { inputs, .. } => inputs.as_slice(),
Self::Foundation { .. } | Self::Leaf { .. } | Self::Attachment { .. } => &[],
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Admittance {
Admitted,
Pending,
Undescribed,
Undefined(Vec<CollectionHandle>),
}
pub trait RecordAdmission {
fn source(&self, collection: CollectionHandle) -> SourceResolution;
fn admits(&self, collection: CollectionHandle, signer: Inline<ED25519PublicKey>) -> Admittance;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SourceResolution {
Root,
Derived(CollectionHandle),
Attached,
Missing(CollectionHandle),
}
#[derive(Clone, Copy, Debug, Default)]
pub struct AdmitEveryRecord;
impl RecordAdmission for AdmitEveryRecord {
fn source(&self, _collection: CollectionHandle) -> SourceResolution {
SourceResolution::Root
}
fn admits(
&self,
_collection: CollectionHandle,
_signer: Inline<ED25519PublicKey>,
) -> Admittance {
Admittance::Admitted
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Parked {
collection: CollectionHandle,
attestation: Attestation,
signer: Inline<ED25519PublicKey>,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
enum Awaiting {
Lineage(CollectionHandle),
Proof(Inline<ED25519PublicKey>),
}
type Edges = PATCH<64, IdentitySchema, ()>;
type Consumers = PATCH<64, IdentitySchema, Edges>;
type Held = PATCH<160, IdentitySchema, ()>;
type Waiters = PATCH<32, IdentitySchema, Held>;
type Joins = PATCH<96, triple_key::Schema, MergeInputs>;
fn join_digest(inputs: &MergeInputs) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
for input in inputs.iter() {
hasher.update(&input.raw);
}
*hasher.finalize().as_bytes()
}
fn edge_key(attestation: &Attestation) -> [u8; 64] {
let mut key = [0u8; 64];
key[..32].copy_from_slice(&attestation.result().raw);
if let Attestation::Join { inputs, .. } = attestation {
key[32..].copy_from_slice(&join_digest(inputs));
}
key
}
fn join_key(collection: CollectionHandle, edge: &[u8; 64]) -> [u8; 96] {
let mut key = [0u8; 96];
key[..32].copy_from_slice(&collection.raw);
key[32..].copy_from_slice(edge);
key
}
const PARKED_FOUNDATION: u8 = 1;
const PARKED_LEAF: u8 = 2;
const PARKED_JOIN: u8 = 3;
const PARKED_ATTACHMENT: u8 = 4;
fn parked_key(entry: &Parked) -> [u8; 160] {
let mut key = [0u8; 160];
key[..32].copy_from_slice(&entry.collection.raw);
key[32..64].copy_from_slice(&entry.signer.raw);
key[64..96].copy_from_slice(&entry.attestation.result().raw);
match &entry.attestation {
Attestation::Foundation { .. } => key[96] = PARKED_FOUNDATION,
Attestation::Leaf { locator, .. } => {
key[96] = PARKED_LEAF;
key[128..].copy_from_slice(locator.as_bytes());
}
Attestation::Join { inputs, .. } => {
key[96] = PARKED_JOIN;
key[128..].copy_from_slice(&join_digest(inputs));
}
Attestation::Attachment { node, .. } => {
key[96] = PARKED_ATTACHMENT;
key[128..].copy_from_slice(&node.raw);
}
}
key
}
fn segment(key: &[u8], at: usize) -> [u8; 32] {
key[at..at + 32]
.try_into()
.expect("a key segment is 32 bytes")
}
fn parked_collection(key: &[u8; 160]) -> CollectionHandle {
Inline::new(segment(key, 0))
}
fn parked_result(key: &[u8; 160]) -> CollectionData {
Inline::new(segment(key, 64))
}
fn held_keys(held: &Held) -> impl Iterator<Item = [u8; 160]> + '_ {
held.iter_ordered().copied()
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct CoverageIndex {
host: Option<Inline<ED25519PublicKey>>,
published: Coverage,
consumers: Consumers,
awaiting_lineage: Waiters,
awaiting_proof: Waiters,
fresh: Waiters,
fresh_joins: Joins,
}
fn parked_in(
waiters: &Waiters,
collection: CollectionHandle,
) -> impl Iterator<Item = [u8; 160]> + '_ {
waiters
.iter_ordered()
.filter_map(|key| waiters.get(key))
.flat_map(held_keys)
.filter(move |key| parked_collection(key) == collection)
}
fn waiting(waiters: &Waiters) -> usize {
waiters
.iter_ordered()
.map(|key| waiters.get(key).map_or(0, |held| held.len() as usize))
.sum()
}
fn drain(waiters: Waiters, into: &mut Vec<[u8; 160]>) {
for key in waiters.iter_ordered() {
if let Some(entries) = waiters.get(key) {
into.extend(held_keys(entries));
}
}
}
impl CoverageIndex {
pub fn new() -> Self {
Self::default()
}
pub fn for_host(host: Inline<ED25519PublicKey>) -> Self {
Self::with_host(Some(host))
}
pub fn with_host(host: Option<Inline<ED25519PublicKey>>) -> Self {
Self {
host,
..Self::default()
}
}
pub fn host(&self) -> Option<Inline<ED25519PublicKey>> {
self.host
}
fn folds(&self, attestation: &Attestation, signer: Inline<ED25519PublicKey>) -> bool {
match attestation {
Attestation::Join { .. } => self.host == Some(signer),
Attestation::Foundation { .. } | Attestation::Leaf { .. } => true,
Attestation::Attachment { .. } => self.host == Some(signer),
}
}
fn keep_fresh_join(&mut self, collection: CollectionHandle, attestation: &Attestation) -> bool {
let Attestation::Join { inputs, .. } = attestation else {
return true;
};
let key = join_key(collection, &edge_key(attestation));
if self.published.joins.get(&key).is_some() {
return false;
}
self.fresh_joins.insert(&Entry::with_value(&key, *inputs));
true
}
fn publish_join(&mut self, collection: CollectionHandle, attestation: &Attestation) {
if let Attestation::Join { inputs, .. } = attestation {
let key = join_key(collection, &edge_key(attestation));
self.fresh_joins.remove(&key);
self.published
.joins
.insert(&Entry::with_value(&key, *inputs));
}
}
fn edge_join(&self, collection: CollectionHandle, edge: &[u8; 64]) -> Option<Attestation> {
let inputs = *self.published.joins.get(&join_key(collection, edge))?;
Some(Attestation::Join {
inputs,
result: Inline::new(segment(edge, 0)),
})
}
fn parked_entry(&self, key: &[u8; 160]) -> Option<Parked> {
let collection = parked_collection(key);
let result = parked_result(key);
let attestation = match key[96] {
PARKED_FOUNDATION => Attestation::Foundation { data: result },
PARKED_LEAF => Attestation::Leaf {
locator: SourceLocator::from_raw(segment(key, 128)),
output: result,
},
PARKED_JOIN => {
let mut edge = [0u8; 64];
edge[..32].copy_from_slice(&result.raw);
edge[32..].copy_from_slice(&key[128..]);
let key = join_key(collection, &edge);
let inputs = *self
.fresh_joins
.get(&key)
.or_else(|| self.published.joins.get(&key))?;
Attestation::Join { inputs, result }
}
PARKED_ATTACHMENT => Attestation::Attachment {
node: Inline::new(segment(key, 128)),
attachment: result,
},
_ => return None,
};
Some(Parked {
collection,
attestation,
signer: Inline::new(segment(key, 32)),
})
}
fn parked_entries(&self, keys: Vec<[u8; 160]>) -> Vec<Parked> {
keys.iter()
.filter_map(|key| self.parked_entry(key))
.collect()
}
#[cfg(test)]
pub(crate) fn joins_reading(
&self,
collection: CollectionHandle,
node: CollectionData,
) -> Vec<(MergeInputs, CollectionData)> {
let Some(edges) = self.consumers.get(&row_key(collection, node)) else {
return Vec::new();
};
edges
.iter_ordered()
.filter_map(|edge| match self.edge_join(collection, edge)? {
Attestation::Join { inputs, result } => Some((inputs, result)),
_ => None,
})
.collect()
}
pub fn stored_joins(&self) -> usize {
let stored = self.published.joins.len() + self.fresh_joins.len();
stored.min(usize::MAX as u64) as usize
}
pub fn coverage(
&self,
collection: CollectionHandle,
node: CollectionData,
) -> Option<&CoverageSet> {
self.published.of(collection, node)
}
pub fn covers(
&self,
collection: CollectionHandle,
node: CollectionData,
foundation: CollectionData,
) -> bool {
self.published.covers(collection, node, foundation)
}
pub fn published(&self) -> &Coverage {
&self.published
}
pub fn len(&self) -> usize {
self.published.len()
}
pub fn is_empty(&self) -> bool {
self.published.is_empty()
}
pub fn has_parked(&self) -> bool {
!self.awaiting_lineage.is_empty()
|| !self.awaiting_proof.is_empty()
|| !self.fresh.is_empty()
}
pub fn has_fresh(&self) -> bool {
!self.fresh.is_empty()
}
pub fn parked(&self) -> usize {
waiting(&self.awaiting_lineage) + waiting(&self.awaiting_proof) + waiting(&self.fresh)
}
pub fn parked_on_signers(&self) -> usize {
waiting(&self.awaiting_proof)
}
pub fn unadmitted_in(&self, collection: CollectionHandle) -> usize {
parked_in(&self.awaiting_proof, collection).count()
}
pub fn unadmitted_nodes_in(&self, collection: CollectionHandle) -> BTreeSet<CollectionData> {
parked_in(&self.awaiting_proof, collection)
.map(|key| parked_result(&key))
.collect()
}
pub fn awaiting_lineage_in(&self, collection: CollectionHandle) -> usize {
parked_in(&self.awaiting_lineage, collection).count()
}
pub fn parked_on_lineages(&self) -> usize {
waiting(&self.awaiting_lineage)
}
pub fn apply<A: RecordAdmission>(&mut self, record: &CollectionRecord, admission: &A) {
self.attest(
record.collection(),
Attestation::of(record),
record.public_key(),
admission,
);
}
pub fn attest<A: RecordAdmission>(
&mut self,
collection: CollectionHandle,
attestation: Attestation,
signer: Inline<ED25519PublicKey>,
admission: &A,
) {
if !self.folds(&attestation, signer) {
return;
}
self.decide(
Parked {
collection,
attestation,
signer,
},
admission,
);
}
fn decide<A: RecordAdmission>(&mut self, entry: Parked, admission: &A) {
match entry.attestation {
Attestation::Attachment { .. } => {
if self.folds(&entry.attestation, entry.signer) {
self.believe(entry.collection, entry.attestation, entry.signer);
}
return;
}
Attestation::Join { .. } => {
if matches!(
admission.source(entry.collection),
SourceResolution::Attached
) {
self.fresh_joins
.remove(&join_key(entry.collection, &edge_key(&entry.attestation)));
return;
}
if self.folds(&entry.attestation, entry.signer) {
self.believe(entry.collection, entry.attestation, entry.signer);
}
return;
}
Attestation::Leaf { .. } => match admission.source(entry.collection) {
SourceResolution::Derived(_) => {}
SourceResolution::Missing(descriptor) => {
self.hold(Awaiting::Lineage(descriptor), entry);
return;
}
SourceResolution::Root | SourceResolution::Attached => {
return;
}
},
Attestation::Foundation { .. } => {
if let SourceResolution::Derived(_) | SourceResolution::Attached =
admission.source(entry.collection)
{
return;
}
}
}
match admission.admits(entry.collection, entry.signer) {
Admittance::Admitted => {}
Admittance::Pending => {
self.hold(Awaiting::Proof(entry.signer), entry);
return;
}
Admittance::Undescribed => {
self.hold(Awaiting::Lineage(entry.collection), entry);
return;
}
Admittance::Undefined(definitions) => {
for definition in definitions {
self.hold(Awaiting::Lineage(definition), entry);
}
self.hold(Awaiting::Proof(entry.signer), entry);
return;
}
}
self.believe(entry.collection, entry.attestation, entry.signer);
}
fn hold(&mut self, awaiting: Awaiting, entry: Parked) {
let (waiters, key) = match awaiting {
Awaiting::Lineage(descriptor) => (&mut self.awaiting_lineage, descriptor.raw),
Awaiting::Proof(signer) => (&mut self.awaiting_proof, signer.raw),
};
let mut entries = waiters.get(&key).cloned().unwrap_or_default();
entries.insert(&Entry::new(&parked_key(&entry)));
waiters.replace(&Entry::with_value(&key, entries));
}
pub fn park(
&mut self,
collection: CollectionHandle,
attestation: Attestation,
signer: Inline<ED25519PublicKey>,
) {
if !self.folds(&attestation, signer) {
return;
}
if !self.keep_fresh_join(collection, &attestation) {
return;
}
let entry = Parked {
collection,
attestation,
signer,
};
let mut held = self.fresh.get(&collection.raw).cloned().unwrap_or_default();
held.insert(&Entry::new(&parked_key(&entry)));
self.fresh
.replace(&Entry::with_value(&collection.raw, held));
}
pub fn park_record(&mut self, record: &CollectionRecord) {
self.park(
record.collection(),
Attestation::of(record),
record.public_key(),
);
}
pub fn blob_arrived(&mut self, handle: CollectionHandle) -> bool {
self.awaiting_lineage.get(&handle.raw).is_some()
}
pub fn proof_arrived(&self) -> bool {
!self.awaiting_proof.is_empty()
}
pub fn settle<A: RecordAdmission>(
&mut self,
admission: &A,
arrivals: impl IntoIterator<Item = CollectionHandle>,
proofs_arrived: bool,
) {
let mut woken = Vec::new();
drain(std::mem::take(&mut self.fresh), &mut woken);
for handle in arrivals {
if let Some(entries) = self.awaiting_lineage.get(&handle.raw).cloned() {
self.awaiting_lineage.remove(&handle.raw);
woken.extend(held_keys(&entries));
}
}
if proofs_arrived {
drain(std::mem::take(&mut self.awaiting_proof), &mut woken);
}
for entry in self.parked_entries(woken) {
self.decide(entry, admission);
}
}
pub fn wake_descriptor(&mut self, descriptor: CollectionHandle) {
let Some(entries) = self.awaiting_lineage.get(&descriptor.raw).cloned() else {
return;
};
self.awaiting_lineage.remove(&descriptor.raw);
for entry in self.parked_entries(held_keys(&entries).collect()) {
self.park(entry.collection, entry.attestation, entry.signer);
}
}
pub fn wake_proofs(&mut self) {
let mut woken = Vec::new();
drain(std::mem::take(&mut self.awaiting_proof), &mut woken);
for entry in self.parked_entries(woken) {
self.park(entry.collection, entry.attestation, entry.signer);
}
}
pub fn settle_collections<A: RecordAdmission>(
&mut self,
admission: &A,
collections: &BTreeSet<CollectionHandle>,
) {
let mut woken = Vec::new();
for collection in collections {
if let Some(entries) = self.fresh.get(&collection.raw).cloned() {
self.fresh.remove(&collection.raw);
woken.extend(held_keys(&entries));
}
}
for entry in self.parked_entries(woken) {
self.decide(entry, admission);
}
}
#[cfg(feature = "object-store")]
pub(crate) fn retry_collections<A: RecordAdmission>(
&mut self,
admission: &A,
collections: &BTreeSet<CollectionHandle>,
) {
let mut woken = Vec::new();
for waiters in [&mut self.awaiting_lineage, &mut self.awaiting_proof] {
let keys: Vec<_> = waiters.iter_ordered().copied().collect();
for key in keys {
let Some(mut entries) = waiters.get(&key).cloned() else {
continue;
};
let selected: Vec<_> = held_keys(&entries)
.filter(|entry| collections.contains(&parked_collection(entry)))
.collect();
for entry in selected {
entries.remove(&entry);
woken.push(entry);
}
if entries.is_empty() {
waiters.remove(&key);
} else {
waiters.replace(&Entry::with_value(&key, entries));
}
}
}
for entry in self.parked_entries(woken) {
self.park(entry.collection, entry.attestation, entry.signer);
}
self.settle_collections(admission, collections);
}
pub fn resolve<A: RecordAdmission>(&mut self, admission: &A) {
let mut woken = Vec::new();
drain(std::mem::take(&mut self.fresh), &mut woken);
drain(std::mem::take(&mut self.awaiting_lineage), &mut woken);
drain(std::mem::take(&mut self.awaiting_proof), &mut woken);
for entry in self.parked_entries(woken) {
self.decide(entry, admission);
}
}
fn believe(
&mut self,
collection: CollectionHandle,
attestation: Attestation,
signer: Inline<ED25519PublicKey>,
) {
if let Attestation::Attachment { node, attachment } = attestation {
self.published.attachments.insert(&Entry::new(&triple(
collection.raw,
node.raw,
attachment.raw,
)));
return;
}
if matches!(attestation, Attestation::Join { .. }) {
self.publish_join(collection, &attestation);
} else {
self.published.owners.insert(&Entry::new(&triple(
collection.raw,
attestation.result().raw,
signer.raw,
)));
}
if let Attestation::Leaf { locator, output } = attestation {
self.published.leaves.insert(&Entry::new(&triple(
collection.raw,
locator.raw(),
output.raw,
)));
}
for input in attestation.inputs() {
let key = row_key(collection, *input);
let mut edges = self.consumers.get(&key).cloned().unwrap_or_default();
edges.insert(&Entry::new(&edge_key(&attestation)));
self.consumers.replace(&Entry::with_value(&key, edges));
}
let mut grown = Vec::new();
if self.drive(collection, attestation) == Driven::Grew {
grown.push(attestation.result());
}
while let Some(node) = grown.pop() {
let Some(edges) = self.consumers.get(&row_key(collection, node)).cloned() else {
continue;
};
for edge in edges.iter_ordered() {
let Some(consumer) = self.edge_join(collection, edge) else {
continue;
};
if self.drive(collection, consumer) == Driven::Grew {
grown.push(consumer.result());
}
}
}
}
fn mark_blocked(
&mut self,
collection: CollectionHandle,
attestation: &Attestation,
blocked: bool,
) {
let key = edge_key(attestation);
let mut set = self
.published
.blocked
.get(&collection.raw)
.cloned()
.unwrap_or_default();
if blocked {
if set.get(&key).is_some() {
return;
}
set.insert(&Entry::new(&key));
} else if set.get(&key).is_some() {
set.remove(&key);
} else {
return;
}
if set.is_empty() {
self.published.blocked.remove(&collection.raw);
} else {
self.published
.blocked
.replace(&Entry::with_value(&collection.raw, set));
}
}
fn drive(&mut self, collection: CollectionHandle, attestation: Attestation) -> Driven {
let contribution = match attestation {
Attestation::Foundation { data } => CoverageSet::from_keys(std::iter::once(data.raw)),
Attestation::Leaf { output, .. } => CoverageSet::from_keys(std::iter::once(output.raw)),
Attestation::Attachment { .. } => return Driven::Unchanged,
Attestation::Join { inputs, .. } => {
let mut union = CoverageSet::new();
for input in inputs.iter() {
let Some(row) = self.published.rows.get(&row_key(collection, input)) else {
self.mark_blocked(collection, &attestation, true);
return Driven::Blocked;
};
union.union(row.clone());
}
self.mark_blocked(collection, &attestation, false);
union
}
};
let key = row_key(collection, attestation.result());
let (support, grew) = match self.published.rows.get(&key) {
Some(existing) => {
let mut merged = existing.clone();
merged.union(contribution);
let grew = merged != *existing;
(merged, grew)
}
None => (contribution, true),
};
if grew {
self.published
.rows
.replace(&Entry::with_value(&key, support));
}
self.advance_frontier(collection, attestation);
if grew {
Driven::Grew
} else {
Driven::Unchanged
}
}
fn advance_frontier(&mut self, collection: CollectionHandle, attestation: Attestation) {
let mut frontier = self
.published
.frontiers
.get(&collection.raw)
.cloned()
.unwrap_or_default();
let result = attestation.result();
if let Attestation::Join { inputs, .. } = attestation {
for input in inputs.iter() {
if input != result {
frontier.remove(&input.raw);
}
}
}
if !self.consumed(collection, result) {
frontier.insert(&Entry::new(&result.raw));
}
self.published
.frontiers
.replace(&Entry::with_value(&collection.raw, frontier));
}
fn consumed(&self, collection: CollectionHandle, node: CollectionData) -> bool {
let Some(edges) = self.consumers.get(&row_key(collection, node)) else {
return false;
};
edges
.iter_ordered()
.filter_map(|edge| self.edge_join(collection, edge))
.any(|consumer| {
let Attestation::Join { inputs, result } = consumer else {
return false;
};
result != node
&& self
.published
.rows
.get(&row_key(collection, result))
.is_some()
&& inputs.iter().filter(|input| *input != node).all(|input| {
self.published
.rows
.get(&row_key(collection, input))
.is_some()
})
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Absent {
Descriptor,
Evidence,
}
pub(crate) struct StoreWriters<'a, R> {
reader: &'a R,
evidence: RefCell<
BTreeMap<CollectionHandle, Result<(AdmissionEvidence, Vec<CollectionHandle>), Absent>>,
>,
sources: RefCell<BTreeMap<CollectionHandle, SourceResolution>>,
admitted: RefCell<BTreeMap<(CollectionHandle, Inline<ED25519PublicKey>), Admittance>>,
}
impl<'a, R: BlobStoreGet + CapabilityProofRead> StoreWriters<'a, R> {
pub(crate) fn new(reader: &'a R) -> Self {
Self {
reader,
evidence: RefCell::new(BTreeMap::new()),
sources: RefCell::new(BTreeMap::new()),
admitted: RefCell::new(BTreeMap::new()),
}
}
fn read_source(&self, collection: CollectionHandle) -> SourceResolution {
let Ok(descriptor) = super::api::load_collection_descriptor(self.reader, collection) else {
return SourceResolution::Missing(collection);
};
let facts = descriptor.fragment.facts();
match super::descriptor::parents(facts) {
Ok(parents) if !parents.is_empty() => return SourceResolution::Attached,
Ok(_) => {}
Err(_) => return SourceResolution::Missing(collection),
}
match super::descriptor::source(facts) {
Ok(Some(source)) => SourceResolution::Derived(source),
Ok(None) => SourceResolution::Root,
Err(_) => SourceResolution::Missing(collection),
}
}
fn write_evidence(&self, collection: CollectionHandle) -> Result<(), Absent> {
let mut cache = self.evidence.borrow_mut();
let entry = cache.entry(collection).or_insert_with(|| {
let descriptor = match super::api::load_collection_descriptor(self.reader, collection) {
Ok(descriptor) => descriptor,
Err(super::api::CollectionDescriptorError::Get { .. }) => {
return Err(Absent::Descriptor)
}
Err(_) => return Err(Absent::Evidence),
};
let facts = descriptor.fragment.facts();
if super::descriptor::sole_descriptor_entity(facts).is_none() {
return Err(Absent::Evidence);
}
let (policies, missing) = super::descriptor::admission_policies_with_missing(
self.reader,
facts,
super::ACTION_WRITE,
None,
);
super::api::discover_admission_evidence(
self.reader,
policies.into_iter(),
super::ACTION_WRITE,
collection,
)
.map(|evidence| (evidence, missing))
.map_err(|_| Absent::Evidence)
});
entry.as_ref().map(|_| ()).map_err(|absent| *absent)
}
fn decide_admits(
&self,
collection: CollectionHandle,
signer: Inline<ED25519PublicKey>,
) -> Admittance {
match self.write_evidence(collection) {
Err(Absent::Descriptor) => return Admittance::Undescribed,
Err(Absent::Evidence) => return Admittance::Pending,
Ok(()) => {}
}
let cache = self.evidence.borrow();
let (evidence, missing) = cache
.get(&collection)
.and_then(|state| state.as_ref().ok())
.expect("write evidence was just resolved");
let Ok(subject) = VerifyingKey::from_bytes(&signer.raw) else {
return Admittance::Pending;
};
let mut definitions = missing.clone();
match evidence.decide(self.reader, subject) {
QuorumOutcome::Met => return Admittance::Admitted,
QuorumOutcome::Unmet => {}
QuorumOutcome::Undefined(handles) => definitions.extend(handles),
}
if definitions.is_empty() {
Admittance::Pending
} else {
Admittance::Undefined(definitions)
}
}
}
impl<R: BlobStoreGet + CapabilityProofRead> RecordAdmission for StoreWriters<'_, R> {
fn source(&self, collection: CollectionHandle) -> SourceResolution {
if let Some(cached) = self.sources.borrow().get(&collection) {
return *cached;
}
let resolved = self.read_source(collection);
if !matches!(resolved, SourceResolution::Missing(_)) {
self.sources.borrow_mut().insert(collection, resolved);
}
resolved
}
fn admits(&self, collection: CollectionHandle, signer: Inline<ED25519PublicKey>) -> Admittance {
let key = (collection, signer);
if let Some(cached) = self.admitted.borrow().get(&key) {
return cached.clone();
}
let answer = self.decide_admits(collection, signer);
self.admitted.borrow_mut().insert(key, answer.clone());
answer
}
}
#[cfg(test)]
pub(crate) fn coverage_of<R>(
reader: &R,
host: Option<Inline<ED25519PublicKey>>,
) -> Result<CoverageIndex, R::RecordsError>
where
R: CollectionRead + BlobStoreGet + CapabilityProofRead,
{
let mut index = CoverageIndex::with_host(host);
for record in reader.records()? {
index.park_record(&record?);
}
index.resolve(&StoreWriters::new(reader));
Ok(index)
}
#[cfg(test)]
mod tests;