use super::{HashBinding, HashError, HashProblem, HashXml, Hashes, check, check_selected};
use crate::pki::CounterPaths;
use crate::prepare::Prepared;
use crate::xml::{Access, Snapshot, Xml};
use structured_asn1::memory::{OwnedVec, Work};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParticipantProblem<N> {
pub signature: N,
pub cause: HashProblem<N>,
}
pub struct ParticipantHashes<N, D, G> {
root: Hashes<N, D, G>,
counters: OwnedVec<Hashes<N, D, G>>,
}
impl<N: Copy, D: Copy, G: Copy> ParticipantHashes<N, D, G> {
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.root.snapshot()
}
#[must_use]
pub fn signature(&self) -> N {
self.root.signature()
}
#[must_use]
pub fn root(&self) -> &Hashes<N, D, G> {
&self.root
}
#[must_use]
pub fn counters(&self) -> &[Hashes<N, D, G>] {
&self.counters
}
}
pub enum ParticipantBinding<N, D, G> {
Bound(ParticipantHashes<N, D, G>),
Unproven(ParticipantProblem<N>),
}
pub type Result<X> = core::result::Result<
ParticipantBinding<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
HashError<<X as Xml>::Error, <X as Xml>::Id>,
>;
pub fn check_participants<X: HashXml, W: Work>(
prepared: &Prepared<X>,
counters: &CounterPaths<'_, '_, X::Id, X::Document, X::Generation>,
access: &Access<'_, X, W>,
) -> Result<X> {
let layout = prepared.bound().containers();
let signature = layout.signature();
if layout.snapshot() != access.snapshot() || counters.snapshot() != access.snapshot() {
return Err(HashError::Foreign { node: signature });
}
if !access
.equal_id(signature, counters.signature())
.map_err(|cause| HashError::Resource {
node: signature,
cause,
})?
{
return Err(HashError::Foreign { node: signature });
}
let mut children = access.execution().vector();
children
.try_reserve_exact(counters.actors().len())
.map_err(|cause| HashError::Resource {
node: signature,
cause,
})?;
let root = match check(prepared.bound(), access)? {
HashBinding::Bound(root) => root,
HashBinding::Unproven(cause) => {
return Ok(ParticipantBinding::Unproven(ParticipantProblem {
signature,
cause,
}));
}
};
for actor in counters.actors() {
let signature = actor.signer().counter().original().child();
access
.execution()
.charge_work(1)
.map_err(|cause| HashError::Resource {
node: signature,
cause,
})?;
match check_selected(signature, access)? {
HashBinding::Bound(receipt) => {
children
.try_push(receipt)
.map_err(|cause| HashError::Resource {
node: signature,
cause,
})?;
}
HashBinding::Unproven(cause) => {
return Ok(ParticipantBinding::Unproven(ParticipantProblem {
signature,
cause,
}));
}
}
}
access
.query(signature, |_, _, _| Ok(()))
.map_err(HashError::Access)?;
Ok(ParticipantBinding::Bound(ParticipantHashes {
root,
counters: children.into_owned(),
}))
}