use super::status::{AuthorityMaterial, archived_authorities};
use super::{ArchiveError, Archived, ArtifactRef, Coverage, Index, IndexError};
use crate::pki::{Participant, ParticipantAuthority, ParticipantClosure};
use crate::source::Query;
use crate::timestamp::{Purpose, TimestampScope, Verified};
use structured_asn1::memory::{HashWork, MemoryError, OwnedVec, Work};
pub struct AnchorMaterial<N, D, G, P> {
participant: Participant<N>,
certificate: Option<Archived<N, D, G, P>>,
}
impl<N: Copy, D, G, P> AnchorMaterial<N, D, G, P> {
#[must_use]
pub fn participant(&self) -> Participant<N> {
self.participant
}
#[must_use]
pub fn certificate(&self) -> Option<&Archived<N, D, G, P>> {
self.certificate.as_ref()
}
}
pub struct ParticipantMaterial<
'closure,
'requirements,
'view,
'certificate,
'document,
N,
D,
G,
P,
I,
> {
inputs: &'closure ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>,
authorities: AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G, P>,
anchors: OwnedVec<AnchorMaterial<N, D, G, P>>,
all_covered: bool,
}
impl<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G, P, I>
ParticipantMaterial<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G, P, I>
{
#[must_use]
pub fn inputs(
&self,
) -> &'closure ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>
{
self.inputs
}
#[must_use]
pub fn authorities(
&self,
) -> &AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G, P> {
&self.authorities
}
#[must_use]
pub fn anchors(&self) -> &[AnchorMaterial<N, D, G, P>] {
&self.anchors
}
#[must_use]
pub fn all_x509_artifacts_covered(&self) -> bool {
self.all_covered
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParticipantArchiveError<N> {
Foreign {
expected: N,
actual: N,
},
Material(ArchiveError<N>),
Resource {
archive: N,
cause: MemoryError,
},
}
pub fn archived_participants<
'closure,
'requirements,
'view,
'certificate,
'document,
N,
D,
G,
P,
O,
W,
Q,
C,
I,
>(
inputs: &'closure ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>,
index: &Index<'_, N, D, G, P, O, W, I>,
coverage: &Coverage<N, D, G, C, I>,
evidence: &Verified<N, D, G, I>,
query: &Q,
) -> Result<
ParticipantMaterial<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G, P, I>,
ParticipantArchiveError<N>,
>
where
N: Copy + Eq + HashWork,
D: Copy + Eq,
G: Copy + Eq,
P: Copy,
W: Work,
C: Work,
Q: Query<Id = N, Document = D, Generation = G>,
{
let required = inputs.requirements();
let scope = required.scope();
let archive = scope.location();
let foreign = || ParticipantArchiveError::Foreign {
expected: archive.node,
actual: evidence.property(),
};
if !matches!(scope, TimestampScope::BeforeArchive(_))
|| archive.snapshot != query.snapshot()
|| archive.snapshot != evidence.token().snapshot
{
return Err(foreign());
}
let resource = |cause| ParticipantArchiveError::Resource {
archive: archive.node,
cause,
};
query.admit(archive.node, 1).map_err(|cause| {
ParticipantArchiveError::Material(ArchiveError::Index(IndexError::Scope(cause)))
})?;
let mut anchors = query.execution().vector();
let cost = archive
.node
.hash_work()
.and_then(|cost| {
cost.checked_add(evidence.property().hash_work()?)
.ok_or(MemoryError::WorkExceeded)
})
.and_then(|cost| {
cost.checked_add(required.signature().hash_work()?)
.ok_or(MemoryError::WorkExceeded)
})
.and_then(|cost| {
cost.checked_add(evidence.signature().hash_work()?)
.ok_or(MemoryError::WorkExceeded)
})
.map_err(resource)?;
query.execution().charge_work(cost).map_err(resource)?;
if archive.node != evidence.property()
|| required.signature() != evidence.signature()
|| evidence.purpose() != Purpose::Archive
{
return Err(foreign());
}
coverage
.require_evidence(evidence, archive.node, query)
.map_err(|cause| ParticipantArchiveError::Material(ArchiveError::Coverage(cause)))?;
let authorities = archived_authorities(inputs.authorities(), index, coverage, evidence, query)
.map_err(ParticipantArchiveError::Material)?;
let mut all_covered = authorities.all_artifacts_covered();
for entry in required.entries() {
query.execution().charge_work(1).map_err(resource)?;
let ParticipantAuthority::Anchor(anchor) = entry.authority() else {
continue;
};
let certificate = match anchor.anchor().certificate() {
Some(certificate) => {
let value = index
.archived(
ArtifactRef::Certificate(certificate),
coverage,
evidence,
query,
)
.map_err(ParticipantArchiveError::Material)?;
if !matches!(value, Archived::Present(_)) {
all_covered = false;
}
Some(value)
}
None => None,
};
anchors
.try_push(AnchorMaterial {
participant: entry.participant(),
certificate,
})
.map_err(resource)?;
}
Ok(ParticipantMaterial {
inputs,
authorities,
anchors: anchors.into_owned(),
all_covered,
})
}