use super::status::{AuthorityMaterial, archived_authorities};
use super::{ArchiveError, Archived, ArtifactRef, Index, IndexError};
use crate::pki::{Participant, ParticipantAuthority, ParticipantClosure};
use crate::timestamp::{ElementCoverage, Role, TimestampScope, Verified};
use crate::xml::{Access, Xml};
use structured_asn1::memory::{HashWork, MemoryError, OwnedVec, Work};
pub struct AnchorMaterial<N, D, G> {
participant: Participant<N>,
certificate: Option<Archived<N, D, G>>,
}
impl<N: Copy, D, G> AnchorMaterial<N, D, G> {
#[must_use]
pub fn participant(&self) -> Participant<N> {
self.participant
}
#[must_use]
pub fn certificate(&self) -> Option<&Archived<N, D, G>> {
self.certificate.as_ref()
}
}
pub struct ParticipantMaterial<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G> {
inputs: &'closure ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G>,
authorities: AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G>,
anchors: OwnedVec<AnchorMaterial<N, D, G>>,
all_covered: bool,
}
impl<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G>
ParticipantMaterial<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G>
{
#[must_use]
pub fn inputs(
&self,
) -> &'closure ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G> {
self.inputs
}
#[must_use]
pub fn authorities(
&self,
) -> &AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G> {
&self.authorities
}
#[must_use]
pub fn anchors(&self) -> &[AnchorMaterial<N, D, G>] {
&self.anchors
}
#[must_use]
pub fn all_x509_artifacts_covered(&self) -> bool {
self.all_covered
}
}
#[derive(Debug)]
pub enum ParticipantArchiveError<E, N> {
Foreign {
expected: N,
actual: N,
},
Material(ArchiveError<E, N>),
Resource {
archive: N,
cause: MemoryError,
},
}
pub type ParticipantArchiveResult<'closure, 'requirements, 'view, 'certificate, 'document, X> =
Result<
ParticipantMaterial<
'closure,
'requirements,
'view,
'certificate,
'document,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
ParticipantArchiveError<<X as Xml>::Error, <X as Xml>::Id>,
>;
pub fn archived_participants<
'closure,
'requirements,
'view,
'certificate,
'document,
X: Xml,
W: Work,
Q: Work,
C: Work,
>(
inputs: &'closure ParticipantClosure<
'requirements,
'view,
'certificate,
'document,
X::Id,
X::Document,
X::Generation,
>,
index: &Index<'_, X::Id, X::Document, X::Generation, W>,
coverage: &ElementCoverage<X::Id, X::Document, X::Generation, C>,
evidence: &Verified<X::Id, X::Document, X::Generation>,
access: &Access<'_, X, Q>,
) -> ParticipantArchiveResult<'closure, 'requirements, 'view, 'certificate, 'document, X> {
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 != access.snapshot()
|| archive.snapshot != evidence.token().snapshot
{
return Err(foreign());
}
let resource = |cause| ParticipantArchiveError::Resource {
archive: archive.node,
cause,
};
let mut anchors = access
.query(archive.node, |_, _, work| Ok(work.vector()))
.map_err(|cause| {
ParticipantArchiveError::Material(ArchiveError::Index(IndexError::Access(cause)))
})?;
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)?;
access.execution().charge_work(cost).map_err(resource)?;
if archive.node != evidence.property()
|| required.signature() != evidence.signature()
|| evidence.role() != Role::Archive
{
return Err(foreign());
}
coverage
.require_evidence(evidence, archive.node, access)
.map_err(|cause| ParticipantArchiveError::Material(ArchiveError::Coverage(cause)))?;
let authorities = archived_authorities(inputs.authorities(), index, coverage, evidence, access)
.map_err(ParticipantArchiveError::Material)?;
let mut all_covered = authorities.all_artifacts_covered();
for entry in required.entries() {
access.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,
access,
)
.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,
})
}