1use super::origin::Origin;
4use super::{AuthorityClosure, AuthorityClosureError, AuthorityNode, CheckedPath, PathStatuses};
5use crate::source::{Location, Query, ScopeError, Snapshot};
6use crate::timestamp::{ObligationError, TimestampPaths, TimestampScope};
7use core::marker::PhantomData;
8use structured_asn1::memory::{HashWork, MemoryError, OwnedVec, Work};
9use structured_pkix::path::EffectiveAnchor;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Participant<N> {
14 Signer(N),
16 Counter {
19 signature: N,
21 property: N,
23 },
24 Credential {
26 signature: N,
28 node: N,
30 authority: usize,
32 },
33 Timestamp {
35 property: N,
37 token: N,
39 },
40}
41
42#[derive(Clone, Copy)]
44pub enum ParticipantAuthority<'path, 'document> {
45 Path(&'path CheckedPath<'document>),
47 Anchor(&'path EffectiveAnchor<'document>),
50 CertificateFree,
53}
54
55#[derive(Clone, Copy)]
58pub enum Authorities<'path, 'document> {
59 Paths(&'path [&'path CheckedPath<'document>]),
62 DirectAnchor(&'path EffectiveAnchor<'document>),
66 CertificateFree,
70}
71
72pub trait SignerReceipt<'path, 'document> {
78 type Id: Copy;
80 type Document: Copy;
82 type Generation: Copy;
84
85 fn signature(&self) -> Location<Self::Id, Self::Document, Self::Generation>;
87 fn path(&self) -> Option<&'path CheckedPath<'document>>;
90}
91
92#[derive(Clone, Copy)]
94pub struct Counter<'path, 'document, N> {
95 pub signature: N,
97 pub property: N,
99 pub path: &'path CheckedPath<'document>,
101}
102
103pub trait CounterReceipts<'path, 'document> {
107 type Id: Copy;
109 type Document: Copy;
111 type Generation: Copy;
113
114 fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
116 fn signature(&self) -> Self::Id;
118 fn len(&self) -> usize;
120 fn is_empty(&self) -> bool {
122 self.len() == 0
123 }
124 fn counter(&self, ordinal: usize) -> Counter<'path, 'document, Self::Id>;
129}
130
131#[derive(Clone, Copy)]
133pub struct Credential<'path, 'document, N> {
134 pub signature: N,
136 pub node: N,
138 pub authorities: Authorities<'path, 'document>,
140}
141
142pub trait CredentialGroup<'path, 'document> {
144 type Id: Copy;
146
147 fn signature(&self) -> Self::Id;
149 fn len(&self) -> usize;
151 fn is_empty(&self) -> bool {
153 self.len() == 0
154 }
155 fn credential(&self, ordinal: usize) -> Credential<'path, 'document, Self::Id>;
160}
161
162pub trait CredentialReceipts<'path, 'document> {
167 type Id: Copy;
169 type Document: Copy;
171 type Generation: Copy;
173 type Group: CredentialGroup<'path, 'document, Id = Self::Id>;
175
176 fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
178 fn signature(&self) -> Self::Id;
180 fn root(&self) -> Option<&Self::Group>;
182 fn counters(&self) -> usize;
184 fn counter(&self, ordinal: usize) -> (Self::Id, &Self::Group);
189}
190
191pub struct ParticipantRequirement<'path, 'document, N> {
193 participant: Participant<N>,
194 authority: ParticipantAuthority<'path, 'document>,
195}
196
197impl<'path, 'document, N: Copy> ParticipantRequirement<'path, 'document, N> {
198 #[must_use]
200 pub fn participant(&self) -> Participant<N> {
201 self.participant
202 }
203 #[must_use]
205 pub fn authority(&self) -> ParticipantAuthority<'path, 'document> {
206 self.authority
207 }
208}
209
210pub struct ParticipantRequirements<'path, 'document, N, D, G, I> {
216 scope: TimestampScope<N, D, G>,
217 signature: N,
218 entries: OwnedVec<ParticipantRequirement<'path, 'document, N>>,
219 origins: Origins<'path>,
220 issuer: PhantomData<I>,
221}
222
223struct Origins<'a> {
224 signer: Origin<'a>,
225 counters: Origin<'a>,
226 credentials: Origin<'a>,
227 timestamps: Origin<'a>,
228}
229
230impl<'path, 'document, N: Copy, D: Copy, G: Copy, I>
231 ParticipantRequirements<'path, 'document, N, D, G, I>
232{
233 #[must_use]
235 pub fn scope(&self) -> TimestampScope<N, D, G> {
236 self.scope
237 }
238 #[must_use]
240 pub fn signature(&self) -> N {
241 self.signature
242 }
243 #[must_use]
245 pub fn entries(&self) -> &[ParticipantRequirement<'path, 'document, N>] {
246 &self.entries
247 }
248
249 #[must_use]
253 pub fn same_credentials<C>(&self, credentials: &C) -> bool {
254 self.origins.credentials.matches(credentials)
255 }
256
257 #[must_use]
260 pub fn same_timestamps(&self, timestamps: &TimestampPaths<'_, '_, '_, N, D, G, I>) -> bool {
261 self.origins.timestamps.matches(timestamps)
262 }
263
264 pub fn require_inputs<S, C, R, Q>(
270 &self,
271 signer: &S,
272 counters: &C,
273 credentials: &R,
274 timestamps: &TimestampPaths<'_, '_, '_, N, D, G, I>,
275 query: &Q,
276 ) -> Result<(), ParticipantError<N>>
277 where
278 D: Eq,
279 G: Eq,
280 Q: Query<Id = N, Document = D, Generation = G>,
281 {
282 let signature = self.signature;
283 if self.scope.location().snapshot != query.snapshot() {
284 return Err(ParticipantError::Foreign { signature });
285 }
286 query.admit(signature, 5)?;
288 if !self.origins.signer.matches(signer)
289 || !self.origins.counters.matches(counters)
290 || !self.same_credentials(credentials)
291 || !self.same_timestamps(timestamps)
292 {
293 return Err(ParticipantError::Foreign { signature });
294 }
295 Ok(())
296 }
297}
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq)]
301pub enum ParticipantError<N> {
302 CounterTimestamps,
304 Obligation(ObligationError<N>),
306 Scope(ScopeError<N>),
308 Foreign {
310 signature: N,
312 },
313 UnprovenSigner,
316 MissingAuthorities {
319 node: N,
321 },
322 Resource {
324 node: N,
326 cause: MemoryError,
328 },
329}
330
331pub type ParticipantResult<'path, 'document, Q, I> = Result<
333 ParticipantRequirements<
334 'path,
335 'document,
336 <Q as Query>::Id,
337 <Q as Query>::Document,
338 <Q as Query>::Generation,
339 I,
340 >,
341 ParticipantError<<Q as Query>::Id>,
342>;
343
344impl<N> From<ScopeError<N>> for ParticipantError<N> {
345 fn from(cause: ScopeError<N>) -> Self {
346 Self::Scope(cause)
347 }
348}
349
350pub struct ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I> {
354 requirements: &'requirements ParticipantRequirements<'view, 'document, N, D, G, I>,
355 authorities: AuthorityClosure<'view, 'certificate, 'document>,
356}
357
358impl<'requirements, 'view, 'certificate, 'document, N, D, G, I>
359 ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>
360{
361 #[must_use]
363 pub fn requirements(
364 &self,
365 ) -> &'requirements ParticipantRequirements<'view, 'document, N, D, G, I> {
366 self.requirements
367 }
368
369 #[must_use]
371 pub fn authorities(&self) -> &AuthorityClosure<'view, 'certificate, 'document> {
372 &self.authorities
373 }
374}
375
376#[derive(Debug)]
378pub enum ParticipantClosureError<N> {
379 Scope(ScopeError<N>),
381 Foreign,
383 Count,
386 Path {
388 participant: usize,
390 },
391 Authority(AuthorityClosureError),
393 Resource(MemoryError),
395}
396
397pub type ClosureResult<'requirements, 'view, 'certificate, 'document, N, D, G, I> = Result<
399 ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>,
400 ParticipantClosureError<N>,
401>;
402
403impl<'path, 'document, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, I>
404 ParticipantRequirements<'path, 'document, N, D, G, I>
405{
406 pub fn close_statuses<'requirements, 'view, 'certificate, Q>(
415 &'requirements self,
416 roots: &'view [&'view PathStatuses<'view, 'certificate, 'document>],
417 supplied: &'view [AuthorityNode<'view, 'certificate, 'document>],
418 query: &Q,
419 ) -> ClosureResult<'requirements, 'view, 'certificate, 'document, N, D, G, I>
420 where
421 Q: Query<Id = N, Document = D, Generation = G>,
422 'path: 'view,
423 {
424 if self.scope.location().snapshot != query.snapshot() {
425 return Err(ParticipantClosureError::Foreign);
426 }
427 query
428 .admit(self.signature, 1)
429 .map_err(ParticipantClosureError::Scope)?;
430 let work = query.execution();
431 let mut cursor = 0;
432 for (participant, entry) in self.entries.iter().enumerate() {
433 work.charge_work(1)
434 .map_err(ParticipantClosureError::Resource)?;
435 let ParticipantAuthority::Path(path) = entry.authority else {
436 continue;
437 };
438 let Some(root) = roots.get(cursor) else {
439 return Err(ParticipantClosureError::Count);
440 };
441 if !core::ptr::eq(path, root.path()) {
442 return Err(ParticipantClosureError::Path { participant });
443 }
444 cursor += 1;
445 }
446 if cursor != roots.len() {
447 return Err(ParticipantClosureError::Count);
448 }
449 let authorities = super::close_authorities(roots, supplied, work)
450 .map_err(ParticipantClosureError::Authority)?;
451 Ok(ParticipantClosure {
452 requirements: self,
453 authorities,
454 })
455 }
456}
457
458struct Collect<'path, 'document, N, W: Work> {
459 entries: OwnedVec<ParticipantRequirement<'path, 'document, N>, W>,
460}
461
462impl<'path, 'document, N: Copy, W: Work> Collect<'path, 'document, N, W> {
463 fn push<Q: Query<Id = N, Work = W>>(
464 &mut self,
465 participant: Participant<N>,
466 authority: ParticipantAuthority<'path, 'document>,
467 node: N,
468 query: &Q,
469 ) -> Result<(), ParticipantError<N>> {
470 query.admit(node, 2)?;
471 self.entries
472 .try_push(ParticipantRequirement {
473 participant,
474 authority,
475 })
476 .map_err(|cause| ParticipantError::Resource { node, cause })
477 }
478
479 fn credentials<Q: Query<Id = N, Work = W>, R: CredentialGroup<'path, 'document, Id = N>>(
480 &mut self,
481 credentials: &R,
482 query: &Q,
483 ) -> Result<(), ParticipantError<N>> {
484 for ordinal in 0..credentials.len() {
485 query.admit(credentials.signature(), 2)?;
486 let credential = credentials.credential(ordinal);
487 let node = credential.node;
488 let participant = |authority| Participant::Credential {
489 signature: credential.signature,
490 node,
491 authority,
492 };
493 match credential.authorities {
494 Authorities::Paths([]) => {
497 return Err(ParticipantError::MissingAuthorities { node });
498 }
499 Authorities::Paths(paths) => {
500 for (index, path) in paths.iter().enumerate() {
501 self.push(
502 participant(index),
503 ParticipantAuthority::Path(path),
504 node,
505 query,
506 )?;
507 }
508 }
509 Authorities::DirectAnchor(anchor) => self.push(
510 participant(0),
511 ParticipantAuthority::Anchor(anchor),
512 node,
513 query,
514 )?,
515 Authorities::CertificateFree => self.push(
516 participant(0),
517 ParticipantAuthority::CertificateFree,
518 node,
519 query,
520 )?,
521 }
522 }
523 Ok(())
524 }
525}
526
527fn counter_and_role_requirements<'path, 'document, C, R, Q, I>(
531 collect: &mut Collect<'path, 'document, Q::Id, Q::Work>,
532 counters: &C,
533 credentials: &R,
534 timestamps: &TimestampPaths<'path, '_, 'document, Q::Id, Q::Document, Q::Generation, I>,
535 query: &Q,
536) -> Result<(), ParticipantError<Q::Id>>
537where
538 Q: Query,
539 C: CounterReceipts<'path, 'document, Id = Q::Id>,
540 R: CredentialReceipts<'path, 'document, Id = Q::Id>,
541{
542 let signature = counters.signature();
543 let mut ordinal = 0;
544 while ordinal < counters.len() {
545 query.admit(signature, 2)?;
546 let selected = timestamps
547 .required()
548 .counter_in_scope(ordinal, query)
549 .map_err(ParticipantError::Obligation)?;
550 let counter = counters.counter(ordinal);
551 ordinal += 1;
552 if !selected {
553 continue;
554 }
555 collect.push(
556 Participant::Counter {
557 signature: counter.signature,
558 property: counter.property,
559 },
560 ParticipantAuthority::Path(counter.path),
561 counter.property,
562 query,
563 )?;
564 }
565 if let Some(root) = credentials.root() {
566 collect.credentials(root, query)?;
567 }
568 let mut credential = 0;
569 let mut ordinal = 0;
570 while ordinal < counters.len() && credential < credentials.counters() {
571 query.admit(signature, 2)?;
572 let (child, group) = credentials.counter(credential);
573 let same = query
574 .equal(counters.counter(ordinal).signature, child)
575 .map_err(|cause| ParticipantError::Resource {
576 node: signature,
577 cause,
578 })?;
579 if same {
580 if timestamps
581 .required()
582 .counter_in_scope(ordinal, query)
583 .map_err(ParticipantError::Obligation)?
584 {
585 collect.credentials(group, query)?;
586 }
587 credential += 1;
588 }
589 ordinal += 1;
590 }
591 if credential != credentials.counters() {
592 return Err(ParticipantError::Foreign { signature });
593 }
594 Ok(())
595}
596
597pub fn participants<'path, 'document, S, C, R, Q, I>(
614 _issuer: I,
615 signer: &'path S,
616 counters: &'path C,
617 credentials: &'path R,
618 timestamps: &'path TimestampPaths<'path, '_, 'document, Q::Id, Q::Document, Q::Generation, I>,
619 query: &Q,
620) -> ParticipantResult<'path, 'document, Q, I>
621where
622 Q: Query,
623 S: SignerReceipt<
624 'path,
625 'document,
626 Id = Q::Id,
627 Document = Q::Document,
628 Generation = Q::Generation,
629 >,
630 C: CounterReceipts<
631 'path,
632 'document,
633 Id = Q::Id,
634 Document = Q::Document,
635 Generation = Q::Generation,
636 >,
637 R: CredentialReceipts<
638 'path,
639 'document,
640 Id = Q::Id,
641 Document = Q::Document,
642 Generation = Q::Generation,
643 >,
644{
645 let selected = signer.signature();
646 let signature = selected.node;
647 let scope = timestamps.required().scope();
648 if selected.snapshot != query.snapshot()
649 || counters.snapshot() != query.snapshot()
650 || credentials.snapshot() != query.snapshot()
651 || scope.location().snapshot != query.snapshot()
652 {
653 return Err(ParticipantError::Foreign { signature });
654 }
655 query.admit(signature, 1)?;
656 let entries = query.execution().vector();
657 for actual in [
658 counters.signature(),
659 credentials.signature(),
660 timestamps.required().signature(),
661 ] {
662 let cost = signature
663 .hash_work()
664 .and_then(|cost| {
665 cost.checked_add(actual.hash_work()?)
666 .ok_or(MemoryError::WorkExceeded)
667 })
668 .map_err(|cause| ParticipantError::Resource {
669 node: signature,
670 cause,
671 })?;
672 query
673 .execution()
674 .charge_work(cost)
675 .map_err(|cause| ParticipantError::Resource {
676 node: signature,
677 cause,
678 })?;
679 if actual != signature {
680 return Err(ParticipantError::Foreign { signature });
681 }
682 }
683 let Some(path) = signer.path() else {
684 return Err(ParticipantError::UnprovenSigner);
685 };
686 if !timestamps
687 .required()
688 .includes_counters(counters, query)
689 .map_err(ParticipantError::Scope)?
690 {
691 return Err(ParticipantError::CounterTimestamps);
692 }
693 let mut collect = Collect { entries };
694 collect.push(
695 Participant::Signer(signature),
696 ParticipantAuthority::Path(path),
697 signature,
698 query,
699 )?;
700 counter_and_role_requirements(&mut collect, counters, credentials, timestamps, query)?;
701 for path in timestamps.paths() {
702 query.admit(signature, 2)?;
703 let source = path.actor().verified();
704 collect.push(
705 Participant::Timestamp {
706 property: source.property(),
707 token: source.token().node,
708 },
709 ParticipantAuthority::Path(path.path()),
710 source.property(),
711 query,
712 )?;
713 }
714 query.admit(signature, 1)?;
715 Ok(ParticipantRequirements {
716 scope,
717 signature,
718 entries: collect.entries.into_owned(),
719 origins: Origins {
720 signer: Origin::of(signer),
721 counters: Origin::of(counters),
722 credentials: Origin::of(credentials),
723 timestamps: Origin::of(timestamps),
724 },
725 issuer: PhantomData,
726 })
727}