use std::cmp::Reverse;
use std::collections::{BTreeMap, BTreeSet, BinaryHeap};
use ed25519_dalek::{SigningKey, VerifyingKey};
use crate::blob::encodings::simplearchive::SimpleArchive;
use crate::blob::encodings::UnknownBlob;
use crate::blob::Blob;
use crate::inline::encodings::hash::Handle;
use crate::inline::Inline;
use crate::patch::Entry;
use crate::repo::{BlobStoreGet, BlobStoreMeta, Store, StoreRead};
use crate::trible::Fragment;
use super::coverage::{Coverage, CoverageIndex, CoverageSet, FrontierSet};
use super::exact_derived::{
data_identity, load_lineage, producer_is_admitted, CollectionRealizationError, Lineage,
Unfetched,
};
use super::operation_snapshot::{OperationFrontier, OperationSnapshot};
use super::ownership::owns;
use super::{
Collection, CollectionData, CollectionDerive, CollectionEncoding, CollectionHandle,
CollectionMap, CollectionMerge, CollectionOperationError, CollectionRecord, DeriveMapping,
MapMapping, SourceLocator,
};
pub const MERGE_FAN_IN: usize = 8;
fn tier(support: u64) -> u32 {
support.max(1).ilog(MERGE_FAN_IN as u64)
}
pub(super) type Node = (CollectionData, CoverageSet);
pub(super) struct Selection {
pub(super) cover: Vec<Node>,
pub(super) covered: CoverageSet,
pub(super) absent: Vec<Node>,
pub(super) dependencies: Vec<CollectionData>,
}
impl Selection {
fn missing(&self, needed: &CoverageSet) -> Vec<CollectionData> {
self.absent
.iter()
.filter(|(_, support)| !support.intersect(needed).is_empty())
.map(|(node, _)| *node)
.collect()
}
pub(super) fn nodes(&self) -> Vec<CollectionData> {
self.cover.iter().map(|(node, _)| *node).collect()
}
}
fn members(set: &CoverageSet) -> Vec<CollectionData> {
set.iter_ordered().map(|raw| Inline::new(*raw)).collect()
}
fn walk<F>(
coverage: &Coverage,
collection: CollectionHandle,
mut take: F,
) -> Result<CoverageSet, CollectionRealizationError>
where
F: FnMut(CollectionData, &CoverageSet) -> Result<bool, CollectionRealizationError>,
{
let mut covered = CoverageSet::new();
let Some(frontier) = coverage.frontier_set(collection) else {
return Ok(covered);
};
let mut pending = BinaryHeap::new();
let mut visited = BTreeSet::new();
for raw in frontier.iter_ordered() {
let node: CollectionData = Inline::new(*raw);
if let Some(support) = coverage.of(collection, node) {
visited.insert(node);
pending.push((support.len(), Reverse(node.raw), node));
}
}
while let Some((_, _, node)) = pending.pop() {
let support = coverage
.of(collection, node)
.expect("a pending node was pushed with its row");
if support <= &covered {
continue;
}
if take(node, support)? {
covered.union(support.clone());
continue;
}
for inputs in coverage.producers(collection, node) {
for input in inputs.iter() {
if input == node || !visited.insert(input) {
continue;
}
if let Some(support) = coverage.of(collection, input) {
pending.push((support.len(), Reverse(input.raw), input));
}
}
}
}
Ok(covered)
}
pub(super) fn select<R, E>(
snapshot: &R,
coverage: &Coverage,
collection: Collection<E>,
) -> Result<Selection, CollectionRealizationError>
where
R: StoreRead,
E: CollectionEncoding,
{
let handle = collection.handle();
let mut cover = Vec::new();
let mut absent = Vec::new();
let mut dependencies = Vec::new();
let resident = match coverage.frontier_set(handle) {
Some(frontier) => snapshot.resident(frontier).map_err(|error| {
CollectionRealizationError::storage("intersect frontier with residency", error)
})?,
None => FrontierSet::new(),
};
let frontier = coverage.frontier_set(handle);
let covered = walk(coverage, handle, |node, support| {
let is_resident = if frontier.is_some_and(|frontier| frontier.get(&node.raw).is_some()) {
resident.get(&node.raw).is_some()
} else {
snapshot
.metadata(Handle::<E>::from_hash(node))
.map_err(|error| {
CollectionRealizationError::storage("inspect lattice node residency", error)
})?
.is_some()
};
if !is_resident {
absent.push((node, support.clone()));
return Ok(false);
}
let missing = E::missing_representation_dependencies(node, snapshot)
.map_err(|error| CollectionRealizationError::Resolution(error.to_string()))?;
if missing.is_empty() {
cover.push((node, support.clone()));
return Ok(true);
}
dependencies.extend(missing);
Ok(false)
})?;
Ok(Selection {
cover,
covered,
absent,
dependencies,
})
}
pub(super) struct AttachedSelection {
pub(super) cover: Vec<(CollectionData, CollectionData)>,
pub(super) covered: CoverageSet,
pub(super) residual: CoverageSet,
}
fn usable_attachment<R, E>(
snapshot: &R,
coverage: &Coverage,
attached: CollectionHandle,
node: CollectionData,
) -> Result<Option<CollectionData>, CollectionRealizationError>
where
R: StoreRead,
E: CollectionEncoding,
{
for attachment in coverage.attachments(attached, node) {
let resident = snapshot
.metadata(Handle::<E>::from_hash(attachment))
.map_err(|error| {
CollectionRealizationError::storage("inspect attachment residency", error)
})?
.is_some();
if !resident {
continue;
}
let missing = E::missing_representation_dependencies(attachment, snapshot)
.map_err(|error| CollectionRealizationError::Resolution(error.to_string()))?;
if missing.is_empty() {
return Ok(Some(attachment));
}
}
Ok(None)
}
fn resident_attachment<R>(
snapshot: &R,
coverage: &Coverage,
attached: CollectionHandle,
node: CollectionData,
) -> Result<Option<CollectionData>, CollectionRealizationError>
where
R: StoreRead,
{
for attachment in coverage.attachments(attached, node) {
if snapshot
.metadata(Handle::<UnknownBlob>::from_hash(attachment))
.map_err(|error| {
CollectionRealizationError::storage("inspect sibling attachment residency", error)
})?
.is_some()
{
return Ok(Some(attachment));
}
}
Ok(None)
}
pub(super) fn select_attached<R, E>(
snapshot: &R,
coverage: &Coverage,
parent: CollectionHandle,
attached: Collection<E>,
) -> Result<AttachedSelection, CollectionRealizationError>
where
R: StoreRead,
E: CollectionEncoding,
{
let mut cover = Vec::new();
let covered = walk(coverage, parent, |node, _| {
match usable_attachment::<R, E>(snapshot, coverage, attached.handle(), node)? {
Some(attachment) => {
cover.push((node, attachment));
Ok(true)
}
None => Ok(false),
}
})?;
let (believed, _) = coverage.frontier_support(parent);
let residual = believed.difference(&covered);
Ok(AttachedSelection {
cover,
covered,
residual,
})
}
fn coverage_of<R>(
snapshot: &R,
scope: &BTreeSet<CollectionHandle>,
) -> Result<Coverage, CollectionRealizationError>
where
R: StoreRead,
{
snapshot
.coverage(scope)
.map_err(|error| CollectionRealizationError::storage("settle coverage", error))
}
fn index_of<R>(
snapshot: &R,
scope: &BTreeSet<CollectionHandle>,
) -> Result<CoverageIndex, CollectionRealizationError>
where
R: StoreRead,
{
snapshot
.index(scope)
.map_err(|error| CollectionRealizationError::storage("settle coverage index", error))
}
fn open<S: Store>(
store: &mut S,
frontier: &OperationFrontier<S::Snapshot>,
operation: &'static str,
) -> Result<OperationSnapshot<S::Snapshot, S::Snapshot>, CollectionRealizationError> {
Ok(frontier.view(
store
.snapshot()
.map_err(|error| CollectionRealizationError::storage(operation, error))?,
))
}
fn publish<S: Store>(
store: &mut S,
frontier: &mut OperationFrontier<S::Snapshot>,
record: CollectionRecord,
operation: &'static str,
) -> Result<(), CollectionRealizationError> {
store
.insert(record)
.map_err(|error| CollectionRealizationError::storage(operation, error))?;
frontier.include_record(record);
Ok(())
}
fn sign_merge(
signing_key: &SigningKey,
collection: CollectionHandle,
inputs: impl IntoIterator<Item = CollectionData>,
result: CollectionData,
) -> Result<CollectionMerge, CollectionRealizationError> {
CollectionMerge::sign(signing_key, collection, inputs, result).map_err(|error| {
CollectionRealizationError::Resolution(format!("plan an invalid MERGE: {error}"))
})
}
struct RootGap {
needed: CoverageSet,
selection: Selection,
}
fn root_gap<R>(
snapshot: &R,
target: Collection<SimpleArchive>,
) -> Result<RootGap, CollectionRealizationError>
where
R: StoreRead,
{
let lineage = load_lineage(snapshot, target)?;
if lineage.foundation != target {
return Err(CollectionRealizationError::InvalidCover(
"a derived SimpleArchive target requires an explicit mapping".into(),
));
}
let coverage = coverage_of(snapshot, &lineage.handles())?;
let (admitted, _) = coverage.frontier_support(target.handle());
let selection = select(snapshot, &coverage, target)?;
let needed = admitted.difference(&selection.covered);
Ok(RootGap { needed, selection })
}
pub(super) fn root_acquisitions<R>(
snapshot: &R,
target: Collection<SimpleArchive>,
) -> Result<Option<Vec<CollectionData>>, CollectionRealizationError>
where
R: StoreRead,
{
let gap = root_gap(snapshot, target)?;
if gap.needed.is_empty() {
return Ok(None);
}
let mut acquisitions = gap.selection.missing(&gap.needed);
acquisitions.extend(gap.selection.dependencies.iter().copied());
acquisitions.extend(members(&gap.needed));
Ok(Some(acquisitions))
}
pub(super) fn root_incomplete<R>(
snapshot: &R,
target: Collection<SimpleArchive>,
) -> Result<(), CollectionRealizationError>
where
R: StoreRead,
{
let gap = root_gap(snapshot, target)?;
if gap.needed.is_empty() {
return Ok(());
}
Err(CollectionRealizationError::IncompleteCover {
missing: gap.selection.missing(&gap.needed),
unsupported_members: members(&gap.needed),
})
}
impl Lineage {
pub(super) fn handles(&self) -> BTreeSet<CollectionHandle> {
self.descriptors.keys().copied().collect()
}
}
fn frontier_nodes<R>(
snapshot: &R,
coverage: &Coverage,
collection: CollectionHandle,
) -> Result<(Vec<Node>, Vec<Node>), CollectionRealizationError>
where
R: StoreRead,
{
let Some(frontier) = coverage.frontier_set(collection) else {
return Ok((Vec::new(), Vec::new()));
};
let resident = snapshot.resident(frontier).map_err(|error| {
CollectionRealizationError::storage("intersect frontier with residency", error)
})?;
let mut every = Vec::new();
let mut held = Vec::new();
for raw in frontier.iter_ordered() {
let node: CollectionData = Inline::new(*raw);
if let Some(support) = coverage.of(collection, node) {
if resident.get(raw).is_some() && reads(snapshot, node)? {
held.push((node, support.clone()));
}
every.push((node, support.clone()));
}
}
for nodes in [&mut every, &mut held] {
nodes.sort_by(|left, right| {
right
.1
.len()
.cmp(&left.1.len())
.then_with(|| left.0.raw.cmp(&right.0.raw))
});
}
Ok((every, held))
}
fn reads<R: StoreRead>(
snapshot: &R,
blob: CollectionData,
) -> Result<bool, CollectionRealizationError> {
Ok(snapshot
.metadata(Handle::<UnknownBlob>::from_hash(blob))
.map_err(|error| CollectionRealizationError::storage("read a resident blob", error))?
.is_some())
}
fn require_host(
index: &CoverageIndex,
signing_key: &SigningKey,
) -> Result<(), CollectionRealizationError> {
let signer = Inline::new(signing_key.verifying_key().to_bytes());
let host = index.host();
if host == Some(signer) {
Ok(())
} else {
Err(CollectionRealizationError::HostMismatch { host, signer })
}
}
pub(super) fn carry_root<S, E>(
store: &mut S,
target: Collection<E>,
signing_key: &SigningKey,
frontier: &mut OperationFrontier<S::Snapshot>,
) -> Result<(), CollectionRealizationError>
where
S: Store,
E: CollectionEncoding,
{
let lineage = load_lineage(&open(store, frontier, "open root-carry snapshot")?, target)?;
if lineage.foundation.handle() != target.handle() {
return Err(CollectionRealizationError::InvalidCover(
"a derived collection carries through its mapping; maintain it with that mapping"
.into(),
));
}
let descriptor = lineage.descriptor(target.handle()).clone();
carry(store, target, signing_key, frontier, |members, reader| {
E::join_many(&descriptor, members, reader)
})
}
fn carry<S, E, J>(
store: &mut S,
target: Collection<E>,
signing_key: &SigningKey,
frontier: &mut OperationFrontier<S::Snapshot>,
mut join: J,
) -> Result<(), CollectionRealizationError>
where
S: Store,
E: CollectionEncoding,
J: FnMut(
&[Blob<E>],
&OperationSnapshot<S::Snapshot, S::Snapshot>,
) -> Result<Blob<E>, CollectionOperationError>,
{
let scope = BTreeSet::from([target.handle()]);
let mut seen = BTreeSet::new();
let mut declined = BTreeSet::<Vec<CollectionData>>::new();
let mut progressed = true;
loop {
let snapshot = open(store, frontier, "open root-carry snapshot")?;
let index = index_of(&snapshot, &scope)?;
let (every, nodes) = frontier_nodes(&snapshot, index.published(), target.handle())?;
let identity: Vec<CollectionData> = every.iter().map(|(node, _)| *node).collect();
if progressed && !seen.insert(identity.clone()) {
return Err(CollectionRealizationError::Stalled { cover: identity });
}
if every.len() < 2 || nodes.is_empty() {
return Ok(());
}
let mut keepers = BTreeMap::<[u8; 32], CollectionData>::new();
for (node, support) in &nodes {
if let Some(root) = support.merkle_root() {
keepers.entry(root).or_insert(*node);
}
}
let absorbed: Vec<(CollectionData, CollectionData)> = every
.iter()
.filter_map(|(node, support)| {
let wider = nodes
.iter()
.take_while(|(_, wider)| wider.len() > support.len())
.find(|(_, wider)| support <= wider)
.map(|(wider, _)| *wider)
.or_else(|| {
let keeper = *keepers.get(&support.merkle_root()?)?;
(keeper != *node).then_some(keeper)
})?;
Some((*node, wider))
})
.collect();
if !absorbed.is_empty() {
require_host(&index, signing_key)?;
drop(snapshot);
for (node, wider) in absorbed {
publish(
store,
frontier,
CollectionRecord::Merge(sign_merge(
signing_key,
target.handle(),
[node, wider],
wider,
)?),
"publish root absorption MERGE",
)?;
}
progressed = true;
continue;
}
let mut tiers = BTreeMap::<u32, BTreeSet<CollectionData>>::new();
for (node, support) in &nodes {
tiers.entry(tier(support.len())).or_default().insert(*node);
}
let round: Vec<Vec<CollectionData>> = tiers
.into_values()
.map(|members| {
let members: Vec<CollectionData> = members.into_iter().collect();
members
.chunks_exact(MERGE_FAN_IN)
.map(<[CollectionData]>::to_vec)
.filter(|group| !declined.contains(group))
.collect::<Vec<_>>()
})
.find(|groups| !groups.is_empty())
.unwrap_or_default();
if round.is_empty() {
return Ok(());
}
require_host(&index, signing_key)?;
drop(snapshot);
progressed = false;
for group in round {
let snapshot = open(store, frontier, "open root-carry join snapshot")?;
let blobs = group
.iter()
.map(|node| snapshot.get::<Blob<E>, E>(Handle::<E>::from_hash(*node)))
.collect::<Result<Vec<_>, _>>()
.map_err(|error| {
CollectionRealizationError::storage("load a held node to carry", error)
})?;
let joined = join(&blobs, &snapshot);
drop(snapshot);
match joined {
Ok(output) => {
let result = data_identity::<E>(&output);
store.put::<E, _>(output).map_err(|error| {
CollectionRealizationError::storage("store a carried root node", error)
})?;
publish(
store,
frontier,
CollectionRecord::Merge(sign_merge(
signing_key,
target.handle(),
group.iter().copied(),
result,
)?),
"publish root carry MERGE",
)?;
progressed = true;
}
Err(CollectionOperationError::Fatal(reason)) => {
return Err(CollectionRealizationError::Merge {
inputs: group,
reason,
});
}
Err(CollectionOperationError::Capacity(_))
| Err(CollectionOperationError::MissingDependency(_)) => {
declined.insert(group);
}
}
}
}
}
pub(super) struct BoundAttachment<M> {
pub(super) parent: Collection<SimpleArchive>,
pub(super) mapping: M,
pub(super) siblings: Vec<CollectionHandle>,
}
pub(super) fn bind_attached<R, M>(
snapshot: &R,
attached: Collection<M::Target>,
) -> Result<BoundAttachment<M>, CollectionRealizationError>
where
R: StoreRead,
M: MapMapping,
{
let (parent, attached_descriptor, parent_descriptor) = attached_lineage(snapshot, attached)?;
let mapping = M::bind(&parent_descriptor, &attached_descriptor).map_err(|error| {
CollectionRealizationError::Resolution(format!(
"attached descriptor does not bind the requested mapping: {error}"
))
})?;
let siblings = mapping.siblings();
Ok(BoundAttachment {
parent,
mapping,
siblings,
})
}
pub(super) fn attached_lineage<R, E>(
snapshot: &R,
attached: Collection<E>,
) -> Result<
(
Collection<SimpleArchive>,
crate::trible::Fragment,
crate::trible::Fragment,
),
CollectionRealizationError,
>
where
R: StoreRead,
E: CollectionEncoding,
{
attached_lineage_mode(snapshot, attached, false)
}
pub(super) fn attached_lineage_acquiring<R, E>(
snapshot: &R,
attached: Collection<E>,
) -> Result<
(
Collection<SimpleArchive>,
crate::trible::Fragment,
crate::trible::Fragment,
),
CollectionRealizationError,
>
where
R: StoreRead,
E: CollectionEncoding,
{
attached_lineage_mode(snapshot, attached, true)
}
fn attached_lineage_mode<R, E>(
snapshot: &R,
attached: Collection<E>,
acquire: bool,
) -> Result<
(
Collection<SimpleArchive>,
crate::trible::Fragment,
crate::trible::Fragment,
),
CollectionRealizationError,
>
where
R: StoreRead,
E: CollectionEncoding,
{
let load = |collection: CollectionHandle| {
if !acquire
&& !snapshot.contains_blob(collection).map_err(|error| {
CollectionRealizationError::storage(
"inspect collection descriptor residency",
error,
)
})?
{
return Err(CollectionRealizationError::MissingDependency {
member: Handle::<SimpleArchive>::to_hash(collection),
});
}
super::api::load_collection_descriptor(snapshot, collection)
.map(|loaded| loaded.fragment)
.map_err(|error| {
if acquire && crate::repo::is_missing_blob(&error) {
return CollectionRealizationError::MissingDependency {
member: Handle::<SimpleArchive>::to_hash(collection),
};
}
CollectionRealizationError::Resolution(format!(
"load collection descriptor {}: {error}",
hex::encode_upper(collection.raw),
))
})
};
let attached_descriptor = load(attached.handle())?;
super::encoding::validate_descriptor_type::<E>(&attached_descriptor).map_err(|error| {
CollectionRealizationError::Resolution(format!(
"attached descriptor has the wrong representation: {error}"
))
})?;
let parents = super::descriptor::parents(attached_descriptor.facts()).map_err(|error| {
CollectionRealizationError::Resolution(format!("decode attached parent: {error}"))
})?;
let parent = match parents.as_slice() {
[parent] => *parent,
[] => {
return Err(CollectionRealizationError::InvalidCover(format!(
"collection {} is not an attached collection",
hex::encode_upper(attached.handle().raw),
)))
}
_ => {
return Err(CollectionRealizationError::InvalidCover(format!(
"attached collection {} names {} parents; an attached read walks one parent's \
lattice",
hex::encode_upper(attached.handle().raw),
parents.len(),
)))
}
};
let parent_descriptor = load(parent)?;
let parent_is_root = super::descriptor::source(parent_descriptor.facts())
.map_err(|error| {
CollectionRealizationError::Resolution(format!("decode parent source: {error}"))
})?
.is_none()
&& super::descriptor::parents(parent_descriptor.facts())
.map_err(|error| {
CollectionRealizationError::Resolution(format!("decode parent kind: {error}"))
})?
.is_empty();
if !parent_is_root {
return Err(CollectionRealizationError::InvalidCover(
"an attached collection's parent must be a root collection".to_owned(),
));
}
super::encoding::validate_descriptor_type::<SimpleArchive>(&parent_descriptor).map_err(
|error| {
CollectionRealizationError::Resolution(format!(
"attached parent is not a SimpleArchive root: {error}"
))
},
)?;
Ok((
Collection::from_handle(parent),
attached_descriptor,
parent_descriptor,
))
}
pub(super) fn attach_frontier<S, M>(
store: &mut S,
attached: Collection<M::Target>,
signing_key: &SigningKey,
unfetched: &Unfetched,
frontier: &mut OperationFrontier<S::Snapshot>,
) -> Result<(), CollectionRealizationError>
where
S: Store,
M: MapMapping,
{
let planning = open(store, frontier, "open attachment planning snapshot")?;
let bound: BoundAttachment<M> = bind_attached(&planning, attached)?;
let parent = bound.parent.handle();
let mut scope = BTreeSet::from([parent, attached.handle()]);
scope.extend(bound.siblings.iter().copied());
let index = index_of(&planning, &scope)?;
let coverage = index.published().clone();
let signer = Inline::new(signing_key.verifying_key().to_bytes());
let host = index.host();
walk(&coverage, parent, |node, _| {
if usable_attachment::<_, M::Target>(&planning, &coverage, attached.handle(), node)?
.is_some()
{
return Ok(true);
}
if host != Some(signer) {
return Err(CollectionRealizationError::HostMismatch { host, signer });
}
let mut siblings = Vec::with_capacity(bound.siblings.len());
for sibling in &bound.siblings {
match resident_attachment(&planning, &coverage, *sibling, node)? {
Some(attachment) => siblings.push(attachment),
None => return Ok(false),
}
}
let snapshot = open(store, frontier, "open attachment mapping snapshot")?;
let handle = Handle::<SimpleArchive>::from_hash(node);
let held = snapshot
.metadata(handle)
.map_err(|error| {
CollectionRealizationError::storage("inspect a parent node's residency", error)
})?
.is_some();
if !held {
return Ok(false);
}
let input: Blob<SimpleArchive> = snapshot.get(handle).map_err(|error| {
CollectionRealizationError::storage("load a parent node to attach", error)
})?;
let output = bound.mapping.map(&input, &siblings, &snapshot);
drop(snapshot);
match output {
Ok(output) => {
let attachment = data_identity::<M::Target>(&output);
store.put::<M::Target, _>(output).map_err(|error| {
CollectionRealizationError::storage("store an attachment", error)
})?;
publish(
store,
frontier,
CollectionRecord::Map(CollectionMap::sign(
signing_key,
attached.handle(),
node,
attachment,
)),
"publish attachment MAP",
)?;
Ok(true)
}
Err(CollectionOperationError::Capacity(_)) => Ok(false),
Err(CollectionOperationError::MissingDependency(member))
if unfetched.contains(&member) =>
{
Ok(false)
}
Err(CollectionOperationError::MissingDependency(member)) => {
Err(CollectionRealizationError::MissingDependency { member })
}
Err(CollectionOperationError::Fatal(reason)) => {
Err(CollectionRealizationError::Derive {
input: node,
reason,
})
}
}
})?;
Ok(())
}
struct Bound<M> {
source: CollectionHandle,
source_is_root: bool,
target_descriptor: Fragment,
mapping: M,
}
fn bind<R, M>(
snapshot: &R,
target: Collection<M::Target>,
) -> Result<Bound<M>, CollectionRealizationError>
where
R: StoreRead,
M: DeriveMapping,
{
let lineage = load_lineage(snapshot, target)?;
let source = lineage
.source_by_target
.get(&target.handle())
.copied()
.ok_or_else(|| {
CollectionRealizationError::Resolution(
"derived maintenance requires a derived target descriptor".to_owned(),
)
})?;
let source_descriptor = lineage.descriptor(source);
let target_descriptor = lineage.descriptor(target.handle());
super::encoding::validate_descriptor_type::<M::Source>(source_descriptor).map_err(|error| {
CollectionRealizationError::Resolution(format!(
"mapping source descriptor has the wrong representation: {error}"
))
})?;
let mapping = M::bind(source_descriptor, target_descriptor).map_err(|error| {
CollectionRealizationError::Resolution(format!(
"target descriptor does not bind the requested mapping: {error}"
))
})?;
Ok(Bound {
source,
source_is_root: source == lineage.foundation.handle(),
target_descriptor: target_descriptor.clone(),
mapping,
})
}
#[allow(clippy::too_many_arguments)]
pub(super) fn maintain_derived<S, M>(
store: &mut S,
target: Collection<M::Target>,
signing_key: &SigningKey,
unfetched: &Unfetched,
wanted: &mut Vec<Vec<CollectionData>>,
frontier: &mut OperationFrontier<S::Snapshot>,
carry_target: bool,
) -> Result<(), CollectionRealizationError>
where
S: Store,
M: DeriveMapping,
{
let bound: Bound<M> = bind(&open(store, frontier, "open mapping snapshot")?, target)?;
let derived = derive_leaves(
store,
target,
&bound,
signing_key,
unfetched,
wanted,
frontier,
carry_target,
)?;
let carried = if carry_target {
carry(store, target, signing_key, frontier, |images, reader| {
bound
.mapping
.join_images(&bound.target_descriptor, images, reader)
})
} else {
Ok(())
};
if matches!(carried, Err(CollectionRealizationError::Storage { .. })) {
return carried;
}
if let Some(member) = derived.missing {
return Err(CollectionRealizationError::MissingDependency { member });
}
carried?;
if let Some(refusal) = derived.refusal {
return Err(refusal);
}
if derived.unauthorized {
return Err(CollectionRealizationError::UnauthorizedProducer {
collection: target.handle(),
});
}
if derived.blocked.is_empty() {
Ok(())
} else {
Err(CollectionRealizationError::Unmappable {
blocked: derived.blocked,
})
}
}
#[derive(Default)]
struct Derivation {
unauthorized: bool,
blocked: Vec<(CollectionData, String)>,
missing: Option<CollectionData>,
refusal: Option<CollectionRealizationError>,
}
fn derive_order(key: &VerifyingKey, foundation: CollectionData) -> [u8; 32] {
let mut hash = blake3::Hasher::new();
hash.update(b"triblespace/derive-order");
hash.update(key.as_bytes());
hash.update(&foundation.raw);
*hash.finalize().as_bytes()
}
#[allow(clippy::too_many_arguments)]
fn derive_leaves<S, M>(
store: &mut S,
target: Collection<M::Target>,
bound: &Bound<M>,
signing_key: &SigningKey,
unfetched: &Unfetched,
wanted: &mut Vec<Vec<CollectionData>>,
frontier: &mut OperationFrontier<S::Snapshot>,
maintain: bool,
) -> Result<Derivation, CollectionRealizationError>
where
S: Store,
M: DeriveMapping,
{
if !bound.mapping.computable_here() {
return Ok(Derivation::default());
}
let key = signing_key.verifying_key();
let snapshot = open(store, frontier, "open leaf-derivation snapshot")?;
let admitted = producer_is_admitted(&snapshot, target, signing_key)?;
if !admitted && maintain {
return Ok(Derivation::default());
}
let scope = BTreeSet::from([bound.source, target.handle()]);
let coverage = coverage_of(&snapshot, &scope)?;
let (foundations, _) = coverage.frontier_support(bound.source);
let mut own = Vec::new();
let mut foreign = Vec::new();
for raw in foundations.iter_ordered() {
let foundation: CollectionData = Inline::new(*raw);
let owned = owns(&coverage, bound.source, foundation, &key);
if !owned && !maintain {
continue;
}
let locator = SourceLocator::of(foundation.raw);
let outputs = coverage.leaf_outputs(target.handle(), locator);
if !maintain && !outputs.is_empty() {
continue;
}
if owned {
own.push((foundation, locator, outputs));
} else {
foreign.push((foundation, locator, outputs));
}
}
if !admitted {
return Ok(Derivation {
unauthorized: !own.is_empty(),
..Derivation::default()
});
}
if !foreign.is_empty() {
let mut payloads = FrontierSet::new();
for (foundation, _, _) in &foreign {
payloads.insert(&Entry::new(&foundation.raw));
}
let resident = snapshot.resident(&payloads).map_err(|error| {
CollectionRealizationError::storage("intersect foreign payloads with residency", error)
})?;
foreign.retain(|(foundation, _, _)| resident.get(&foundation.raw).is_some());
}
let mut images = FrontierSet::new();
for (_, _, outputs) in own.iter().chain(&foreign) {
for output in outputs {
images.insert(&Entry::new(&output.raw));
}
}
let resident = if images.is_empty() {
FrontierSet::new()
} else {
snapshot.resident(&images).map_err(|error| {
CollectionRealizationError::storage("intersect leaf outputs with residency", error)
})?
};
let mut readable = FrontierSet::new();
for raw in resident.iter_ordered() {
if reads(&snapshot, Inline::new(*raw))? {
readable.insert(&Entry::new(raw));
}
}
drop(snapshot);
own.sort_by_cached_key(|(foundation, _, _)| derive_order(&key, *foundation));
foreign.sort_by_cached_key(|(foundation, _, _)| derive_order(&key, *foundation));
let mut owed = Vec::new();
for (entries, owned) in [(own, true), (foreign, false)] {
for (foundation, locator, outputs) in entries {
if outputs
.iter()
.any(|output| readable.get(&output.raw).is_some())
{
continue;
}
let unasked = outputs
.iter()
.filter(|output| !unfetched.unavailable.contains(*output))
.copied()
.collect::<Vec<_>>();
if unasked.is_empty() {
owed.push((foundation, locator, outputs, owned));
} else {
wanted.push(unasked);
}
}
}
let mut derivation = Derivation::default();
for (foundation, locator, existing, own) in owed {
match derive_leaf(
store,
frontier,
target,
bound,
signing_key,
foundation,
locator,
&existing,
true,
)? {
Mapped::Done | Mapped::Overtaken => {}
Mapped::Absent | Mapped::Refused(_) if !own => {}
Mapped::Absent if unfetched.contains(&foundation) => {
if bound.source_is_root {
derivation.blocked.push((
foundation,
"the source commit's payload cannot be read here or acquired".to_owned(),
));
}
}
Mapped::Absent => {
derivation.missing.get_or_insert(foundation);
}
Mapped::Refused(error) => {
match refused(foundation, error, unfetched, &mut derivation.blocked) {
Ok(()) => {}
Err(CollectionRealizationError::MissingDependency { member }) => {
derivation.missing.get_or_insert(member);
}
Err(error) => {
derivation.refusal.get_or_insert(error);
}
}
}
}
}
Ok(derivation)
}
enum Mapped {
Absent,
Done,
Overtaken,
Refused(CollectionOperationError),
}
fn overtaken<R>(
now: &R,
target: CollectionHandle,
locator: SourceLocator,
existing: &[CollectionData],
) -> Result<bool, CollectionRealizationError>
where
R: StoreRead,
{
let coverage = coverage_of(now, &BTreeSet::from([target]))?;
for output in coverage.leaf_outputs(target, locator) {
if !existing.contains(&output) {
return Ok(true);
}
if now
.metadata(Handle::<UnknownBlob>::from_hash(output))
.map_err(|error| CollectionRealizationError::storage("inspect a leaf output", error))?
.is_some()
{
return Ok(true);
}
}
Ok(false)
}
#[allow(clippy::too_many_arguments)]
fn derive_leaf<S, M>(
store: &mut S,
frontier: &mut OperationFrontier<S::Snapshot>,
target: Collection<M::Target>,
bound: &Bound<M>,
signing_key: &SigningKey,
foundation: CollectionData,
locator: SourceLocator,
existing: &[CollectionData],
recheck: bool,
) -> Result<Mapped, CollectionRealizationError>
where
S: Store,
M: DeriveMapping,
{
let now = store.snapshot().map_err(|error| {
CollectionRealizationError::storage("open leaf mapping snapshot", error)
})?;
if recheck && overtaken(&now, target.handle(), locator, existing)? {
return Ok(Mapped::Overtaken);
}
let snapshot = frontier.view(now);
let handle = Handle::<M::Source>::from_hash(foundation);
if snapshot
.metadata(handle)
.map_err(|error| CollectionRealizationError::storage("inspect a source foundation", error))?
.is_none()
{
return Ok(Mapped::Absent);
}
let input: Blob<M::Source> = snapshot
.get(handle)
.map_err(|error| CollectionRealizationError::storage("load a source foundation", error))?;
let output = bound.mapping.map(&input, &snapshot);
drop(snapshot);
let output = match output {
Ok(output) => output,
Err(error) => return Ok(Mapped::Refused(error)),
};
let output_data = data_identity::<M::Target>(&output);
store
.put::<M::Target, _>(output)
.map_err(|error| CollectionRealizationError::storage("store a leaf image", error))?;
if !existing.contains(&output_data) {
publish(
store,
frontier,
CollectionRecord::Derive(CollectionDerive::sign(
signing_key,
target.handle(),
locator,
output_data,
)),
"publish leaf DERIVE",
)?;
}
Ok(Mapped::Done)
}
fn refused(
foundation: CollectionData,
error: CollectionOperationError,
unfetched: &Unfetched,
blocked: &mut Vec<(CollectionData, String)>,
) -> Result<(), CollectionRealizationError> {
match error {
CollectionOperationError::Capacity(reason) => blocked.push((foundation, reason)),
CollectionOperationError::MissingDependency(member) if unfetched.contains(&member) => {
blocked.push((
foundation,
format!(
"the mapping requires blob {}, which could not be acquired",
hex::encode_upper(member.raw)
),
))
}
CollectionOperationError::MissingDependency(member) => {
return Err(CollectionRealizationError::MissingDependency { member })
}
CollectionOperationError::Fatal(reason) => {
return Err(CollectionRealizationError::Derive {
input: foundation,
reason,
})
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub(super) fn rederive<S, M>(
store: &mut S,
target: Collection<M::Target>,
source: CollectionHandle,
foundations: &BTreeSet<CollectionData>,
done: &mut BTreeSet<CollectionData>,
signing_key: &SigningKey,
unfetched: &Unfetched,
frontier: &mut OperationFrontier<S::Snapshot>,
) -> Result<(), CollectionRealizationError>
where
S: Store,
M: DeriveMapping,
{
let snapshot = open(store, frontier, "open re-derivation snapshot")?;
let bound: Bound<M> = bind(&snapshot, target)?;
if !bound.mapping.computable_here() {
return Err(CollectionRealizationError::Resolution(
"the mapping is pinned to a class of host this one is not; re-derive on a host \
that can compute it"
.to_owned(),
));
}
if source != bound.source {
return Err(CollectionRealizationError::InvalidCover(format!(
"the foundations name collection {}, but the target derives from {}",
hex::encode_upper(source.raw),
hex::encode_upper(bound.source.raw),
)));
}
let scope = BTreeSet::from([bound.source, target.handle()]);
let coverage = coverage_of(&snapshot, &scope)?;
let (believed, _) = coverage.frontier_support(bound.source);
if let Some(stranger) = foundations
.iter()
.find(|foundation| believed.get(&foundation.raw).is_none())
{
return Err(CollectionRealizationError::InvalidCover(format!(
"{} is not a believed foundation of the target's source",
hex::encode_upper(stranger.raw),
)));
}
if !producer_is_admitted(&snapshot, target, signing_key)? {
return Err(CollectionRealizationError::UnauthorizedProducer {
collection: target.handle(),
});
}
let owed: Vec<_> = foundations
.iter()
.filter(|foundation| !done.contains(*foundation))
.map(|foundation| {
let locator = SourceLocator::of(foundation.raw);
(
*foundation,
locator,
coverage.leaf_outputs(target.handle(), locator),
)
})
.collect();
let mut payloads = FrontierSet::new();
for (foundation, _, _) in &owed {
payloads.insert(&Entry::new(&foundation.raw));
}
let resident = snapshot.resident(&payloads).map_err(|error| {
CollectionRealizationError::storage("intersect named payloads with residency", error)
})?;
if let Some((member, _, _)) = owed.iter().find(|(foundation, _, _)| {
resident.get(&foundation.raw).is_none() && !unfetched.contains(foundation)
}) {
return Err(CollectionRealizationError::MissingDependency { member: *member });
}
drop(snapshot);
let mut blocked = Vec::new();
for (foundation, locator, existing) in owed {
match derive_leaf(
store,
frontier,
target,
&bound,
signing_key,
foundation,
locator,
&existing,
false,
)? {
Mapped::Done | Mapped::Overtaken => {
done.insert(foundation);
}
Mapped::Absent if unfetched.contains(&foundation) => blocked.push((
foundation,
"the source foundation's payload cannot be read here or acquired".to_owned(),
)),
Mapped::Absent => {
return Err(CollectionRealizationError::MissingDependency { member: foundation })
}
Mapped::Refused(error) => refused(foundation, error, unfetched, &mut blocked)?,
}
}
if blocked.is_empty() {
Ok(())
} else {
Err(CollectionRealizationError::Unmappable { blocked })
}
}
#[cfg(test)]
mod tests {
use super::tier;
#[test]
fn tiers_are_powers_of_the_fan_in() {
assert_eq!(tier(0), 0);
assert_eq!(tier(1), 0);
assert_eq!(tier(7), 0);
assert_eq!(tier(8), 1);
assert_eq!(tier(63), 1);
assert_eq!(tier(64), 2);
assert_eq!(tier(511), 2);
assert_eq!(tier(512), 3);
}
}