1use core::num::NonZeroU64;
2
3use crate::wire::{
4 AttachEnvelope, CredentialAttachRequest, CredentialAttachResponse, EnrollmentEnvelope,
5 EnrollmentReceiptCapacityScope, EnrollmentRequest, EnrollmentResponse,
6 IdentityCapacityExceeded, IdentityCapacityScope, ParticipantId, ReceiptCapacityScope,
7};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum CapacityCounterInvariantError {
12 ZeroLimit,
14 OccupiedExceedsLimit {
16 occupied: u64,
18 limit: u64,
20 },
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub struct CapacityCounter {
26 limit: NonZeroU64,
27 occupied: u64,
28}
29
30impl CapacityCounter {
31 pub const fn try_new(limit: u64, occupied: u64) -> Result<Self, CapacityCounterInvariantError> {
39 let Some(limit) = NonZeroU64::new(limit) else {
40 return Err(CapacityCounterInvariantError::ZeroLimit);
41 };
42 if occupied > limit.get() {
43 return Err(CapacityCounterInvariantError::OccupiedExceedsLimit {
44 occupied,
45 limit: limit.get(),
46 });
47 }
48 Ok(Self { limit, occupied })
49 }
50
51 #[must_use]
53 pub const fn limit(self) -> u64 {
54 self.limit.get()
55 }
56
57 #[must_use]
59 pub const fn occupied(self) -> u64 {
60 self.occupied
61 }
62
63 #[must_use]
65 pub const fn is_full(self) -> bool {
66 self.occupied == self.limit.get()
67 }
68
69 const fn incremented(self) -> Option<Self> {
70 if self.is_full() {
71 return None;
72 }
73 Some(Self {
74 limit: self.limit,
75 occupied: self.occupied + 1,
76 })
77 }
78}
79
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
82pub enum FreshParticipantCapacityCounterInvariantError {
83 Capacity(CapacityCounterInvariantError),
85 Nonempty {
87 occupied: u64,
89 },
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97pub struct FreshParticipantCapacityCounter {
98 counter: CapacityCounter,
99}
100
101impl FreshParticipantCapacityCounter {
102 pub const fn try_new(
111 limit: u64,
112 occupied: u64,
113 ) -> Result<Self, FreshParticipantCapacityCounterInvariantError> {
114 let counter = match CapacityCounter::try_new(limit, occupied) {
115 Ok(counter) => counter,
116 Err(error) => {
117 return Err(FreshParticipantCapacityCounterInvariantError::Capacity(
118 error,
119 ));
120 }
121 };
122 if occupied != 0 {
123 return Err(FreshParticipantCapacityCounterInvariantError::Nonempty { occupied });
124 }
125 Ok(Self { counter })
126 }
127
128 #[must_use]
130 pub const fn limit(self) -> u64 {
131 self.counter.limit()
132 }
133
134 #[must_use]
136 pub const fn occupied(self) -> u64 {
137 self.counter.occupied()
138 }
139
140 const fn reserved(self) -> CapacityCounter {
141 CapacityCounter {
142 limit: self.counter.limit,
143 occupied: 1,
144 }
145 }
146}
147
148#[derive(Clone, Copy, Debug, PartialEq, Eq)]
150pub enum ConnectionConversationTracking {
151 AlreadyTracked,
153 Untracked,
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Eq)]
159pub struct ConnectionConversationCapacityCommit {
160 resulting: CapacityCounter,
161 newly_tracked: bool,
162}
163
164impl ConnectionConversationCapacityCommit {
165 #[must_use]
167 pub const fn resulting(self) -> CapacityCounter {
168 self.resulting
169 }
170
171 #[must_use]
173 pub const fn newly_tracked(self) -> bool {
174 self.newly_tracked
175 }
176}
177
178#[derive(Clone, Debug, PartialEq, Eq)]
185pub enum SemanticConnectionCapacityDecision {
186 Commit(ConnectionConversationCapacityCommit),
188 Respond {
190 limit: u64,
192 },
193}
194
195#[must_use]
202pub const fn select_semantic_connection_capacity(
203 tracking: ConnectionConversationTracking,
204 current: CapacityCounter,
205) -> SemanticConnectionCapacityDecision {
206 match tracking {
207 ConnectionConversationTracking::AlreadyTracked => {
208 SemanticConnectionCapacityDecision::Commit(ConnectionConversationCapacityCommit {
209 resulting: current,
210 newly_tracked: false,
211 })
212 }
213 ConnectionConversationTracking::Untracked => {
214 let Some(resulting) = current.incremented() else {
215 return SemanticConnectionCapacityDecision::Respond {
216 limit: current.limit(),
217 };
218 };
219 SemanticConnectionCapacityDecision::Commit(ConnectionConversationCapacityCommit {
220 resulting,
221 newly_tracked: true,
222 })
223 }
224 }
225}
226
227#[derive(Clone, Copy, Debug, PartialEq, Eq)]
229pub enum BindingSlotOccupancy {
230 Empty,
232 Occupied {
234 participant_id: ParticipantId,
236 },
237}
238
239#[derive(Clone, Debug, PartialEq, Eq)]
242pub enum BindingSlotDecision<R> {
243 Available,
245 Respond(R),
247}
248
249#[must_use]
251pub const fn select_enrollment_binding_slot(
252 request: &EnrollmentRequest,
253 occupancy: BindingSlotOccupancy,
254) -> BindingSlotDecision<EnrollmentResponse> {
255 match occupancy {
256 BindingSlotOccupancy::Empty => BindingSlotDecision::Available,
257 BindingSlotOccupancy::Occupied { .. } => BindingSlotDecision::Respond(
258 EnrollmentResponse::connection_conversation_binding_occupied(&enrollment_envelope(
259 request,
260 )),
261 ),
262 }
263}
264
265#[must_use]
268pub const fn select_credential_attach_binding_slot(
269 request: &CredentialAttachRequest,
270 occupancy: BindingSlotOccupancy,
271) -> BindingSlotDecision<CredentialAttachResponse> {
272 match occupancy {
273 BindingSlotOccupancy::Empty => BindingSlotDecision::Available,
274 BindingSlotOccupancy::Occupied { participant_id }
275 if participant_id == request.participant_id =>
276 {
277 BindingSlotDecision::Available
278 }
279 BindingSlotOccupancy::Occupied { .. } => BindingSlotDecision::Respond(
280 CredentialAttachResponse::connection_conversation_binding_occupied(&attach_envelope(
281 request,
282 )),
283 ),
284 }
285}
286
287#[derive(Clone, Copy, Debug, PartialEq, Eq)]
293pub struct EnrollmentCapacityCounters {
294 identity_server: CapacityCounter,
295 identity_conversation: CapacityCounter,
296 live_receipt_server: CapacityCounter,
297 live_receipt_participant: FreshParticipantCapacityCounter,
298 provenance_server: CapacityCounter,
299 provenance_conversation: CapacityCounter,
300 provenance_participant: FreshParticipantCapacityCounter,
301}
302
303impl EnrollmentCapacityCounters {
304 #[must_use]
306 pub const fn new(
307 identity_server: CapacityCounter,
308 identity_conversation: CapacityCounter,
309 live_receipt_server: CapacityCounter,
310 live_receipt_participant: FreshParticipantCapacityCounter,
311 provenance_server: CapacityCounter,
312 provenance_conversation: CapacityCounter,
313 provenance_participant: FreshParticipantCapacityCounter,
314 ) -> Self {
315 Self {
316 identity_server,
317 identity_conversation,
318 live_receipt_server,
319 live_receipt_participant,
320 provenance_server,
321 provenance_conversation,
322 provenance_participant,
323 }
324 }
325
326 #[must_use]
328 pub const fn identity_server(self) -> CapacityCounter {
329 self.identity_server
330 }
331
332 #[must_use]
334 pub const fn identity_conversation(self) -> CapacityCounter {
335 self.identity_conversation
336 }
337
338 #[must_use]
340 pub const fn live_receipt_server(self) -> CapacityCounter {
341 self.live_receipt_server
342 }
343
344 #[must_use]
346 pub const fn live_receipt_participant(self) -> FreshParticipantCapacityCounter {
347 self.live_receipt_participant
348 }
349
350 #[must_use]
352 pub const fn provenance_server(self) -> CapacityCounter {
353 self.provenance_server
354 }
355
356 #[must_use]
358 pub const fn provenance_conversation(self) -> CapacityCounter {
359 self.provenance_conversation
360 }
361
362 #[must_use]
364 pub const fn provenance_participant(self) -> FreshParticipantCapacityCounter {
365 self.provenance_participant
366 }
367}
368
369#[derive(Clone, Copy, Debug, PartialEq, Eq)]
371pub struct ResultingEnrollmentCapacityCounters {
372 identity_server: CapacityCounter,
373 identity_conversation: CapacityCounter,
374 live_receipt_server: CapacityCounter,
375 live_receipt_participant: CapacityCounter,
376 provenance_server: CapacityCounter,
377 provenance_conversation: CapacityCounter,
378 provenance_participant: CapacityCounter,
379}
380
381impl ResultingEnrollmentCapacityCounters {
382 #[must_use]
384 pub const fn identity_server(self) -> CapacityCounter {
385 self.identity_server
386 }
387
388 #[must_use]
390 pub const fn identity_conversation(self) -> CapacityCounter {
391 self.identity_conversation
392 }
393
394 #[must_use]
396 pub const fn live_receipt_server(self) -> CapacityCounter {
397 self.live_receipt_server
398 }
399
400 #[must_use]
402 pub const fn live_receipt_participant(self) -> CapacityCounter {
403 self.live_receipt_participant
404 }
405
406 #[must_use]
408 pub const fn provenance_server(self) -> CapacityCounter {
409 self.provenance_server
410 }
411
412 #[must_use]
414 pub const fn provenance_conversation(self) -> CapacityCounter {
415 self.provenance_conversation
416 }
417
418 #[must_use]
420 pub const fn provenance_participant(self) -> CapacityCounter {
421 self.provenance_participant
422 }
423}
424
425#[derive(Clone, Copy, Debug, PartialEq, Eq)]
427pub struct EnrollmentCapacityCommit {
428 resulting: ResultingEnrollmentCapacityCounters,
429}
430
431impl EnrollmentCapacityCommit {
432 #[must_use]
434 pub const fn resulting(self) -> ResultingEnrollmentCapacityCounters {
435 self.resulting
436 }
437}
438
439#[derive(Clone, Debug, PartialEq, Eq)]
441pub enum EnrollmentCapacityDecision {
442 Commit(EnrollmentCapacityCommit),
444 Respond(EnrollmentResponse),
446}
447
448#[must_use]
454pub const fn select_enrollment_capacity(
455 request: &EnrollmentRequest,
456 current: EnrollmentCapacityCounters,
457) -> EnrollmentCapacityDecision {
458 let Some(identity_server) = current.identity_server.incremented() else {
459 return enrollment_identity_refusal(
460 request,
461 IdentityCapacityScope::Server,
462 current.identity_server,
463 );
464 };
465 let Some(identity_conversation) = current.identity_conversation.incremented() else {
466 return enrollment_identity_refusal(
467 request,
468 IdentityCapacityScope::Conversation,
469 current.identity_conversation,
470 );
471 };
472 let Some(live_receipt_server) = current.live_receipt_server.incremented() else {
473 return enrollment_receipt_refusal(
474 request,
475 EnrollmentReceiptCapacityScope::LiveReceiptServer,
476 current.live_receipt_server,
477 );
478 };
479 let Some(provenance_server) = current.provenance_server.incremented() else {
480 return enrollment_receipt_refusal(
481 request,
482 EnrollmentReceiptCapacityScope::ProvenanceServer,
483 current.provenance_server,
484 );
485 };
486 let Some(provenance_conversation) = current.provenance_conversation.incremented() else {
487 return enrollment_receipt_refusal(
488 request,
489 EnrollmentReceiptCapacityScope::ProvenanceConversation,
490 current.provenance_conversation,
491 );
492 };
493
494 EnrollmentCapacityDecision::Commit(EnrollmentCapacityCommit {
495 resulting: ResultingEnrollmentCapacityCounters {
496 identity_server,
497 identity_conversation,
498 live_receipt_server,
499 live_receipt_participant: current.live_receipt_participant.reserved(),
500 provenance_server,
501 provenance_conversation,
502 provenance_participant: current.provenance_participant.reserved(),
503 },
504 })
505}
506
507#[derive(Clone, Copy, Debug, PartialEq, Eq)]
509pub struct CredentialAttachCapacityCounters {
510 live_receipt_server: CapacityCounter,
511 live_receipt_participant: CapacityCounter,
512 provenance_server: CapacityCounter,
513 provenance_conversation: CapacityCounter,
514 provenance_participant: CapacityCounter,
515}
516
517impl CredentialAttachCapacityCounters {
518 #[must_use]
520 pub const fn new(
521 live_receipt_server: CapacityCounter,
522 live_receipt_participant: CapacityCounter,
523 provenance_server: CapacityCounter,
524 provenance_conversation: CapacityCounter,
525 provenance_participant: CapacityCounter,
526 ) -> Self {
527 Self {
528 live_receipt_server,
529 live_receipt_participant,
530 provenance_server,
531 provenance_conversation,
532 provenance_participant,
533 }
534 }
535
536 #[must_use]
538 pub const fn live_receipt_server(self) -> CapacityCounter {
539 self.live_receipt_server
540 }
541
542 #[must_use]
544 pub const fn live_receipt_participant(self) -> CapacityCounter {
545 self.live_receipt_participant
546 }
547
548 #[must_use]
550 pub const fn provenance_server(self) -> CapacityCounter {
551 self.provenance_server
552 }
553
554 #[must_use]
556 pub const fn provenance_conversation(self) -> CapacityCounter {
557 self.provenance_conversation
558 }
559
560 #[must_use]
562 pub const fn provenance_participant(self) -> CapacityCounter {
563 self.provenance_participant
564 }
565}
566
567#[derive(Clone, Copy, Debug, PartialEq, Eq)]
569pub struct CredentialAttachCapacityCommit {
570 resulting: CredentialAttachCapacityCounters,
571}
572
573impl CredentialAttachCapacityCommit {
574 #[must_use]
576 pub const fn resulting(self) -> CredentialAttachCapacityCounters {
577 self.resulting
578 }
579}
580
581#[derive(Clone, Debug, PartialEq, Eq)]
583pub enum CredentialAttachCapacityDecision {
584 Commit(CredentialAttachCapacityCommit),
586 Respond(CredentialAttachResponse),
588}
589
590#[must_use]
592pub const fn select_credential_attach_capacity(
593 request: &CredentialAttachRequest,
594 current: CredentialAttachCapacityCounters,
595) -> CredentialAttachCapacityDecision {
596 let Some(live_receipt_server) = current.live_receipt_server.incremented() else {
597 return credential_attach_receipt_refusal(
598 request,
599 ReceiptCapacityScope::LiveReceiptServer,
600 current.live_receipt_server,
601 );
602 };
603 let Some(live_receipt_participant) = current.live_receipt_participant.incremented() else {
604 return credential_attach_receipt_refusal(
605 request,
606 ReceiptCapacityScope::LiveReceiptParticipant,
607 current.live_receipt_participant,
608 );
609 };
610 let Some(provenance_server) = current.provenance_server.incremented() else {
611 return credential_attach_receipt_refusal(
612 request,
613 ReceiptCapacityScope::ProvenanceServer,
614 current.provenance_server,
615 );
616 };
617 let Some(provenance_conversation) = current.provenance_conversation.incremented() else {
618 return credential_attach_receipt_refusal(
619 request,
620 ReceiptCapacityScope::ProvenanceConversation,
621 current.provenance_conversation,
622 );
623 };
624 let Some(provenance_participant) = current.provenance_participant.incremented() else {
625 return credential_attach_receipt_refusal(
626 request,
627 ReceiptCapacityScope::ProvenanceParticipant,
628 current.provenance_participant,
629 );
630 };
631
632 CredentialAttachCapacityDecision::Commit(CredentialAttachCapacityCommit {
633 resulting: CredentialAttachCapacityCounters {
634 live_receipt_server,
635 live_receipt_participant,
636 provenance_server,
637 provenance_conversation,
638 provenance_participant,
639 },
640 })
641}
642
643const fn enrollment_identity_refusal(
644 request: &EnrollmentRequest,
645 scope: IdentityCapacityScope,
646 counter: CapacityCounter,
647) -> EnrollmentCapacityDecision {
648 EnrollmentCapacityDecision::Respond(EnrollmentResponse::identity_capacity_exceeded(
649 IdentityCapacityExceeded {
650 request: enrollment_envelope(request),
651 scope,
652 limit: counter.limit(),
653 occupied: counter.occupied(),
654 },
655 ))
656}
657
658const fn enrollment_receipt_refusal(
659 request: &EnrollmentRequest,
660 scope: EnrollmentReceiptCapacityScope,
661 counter: CapacityCounter,
662) -> EnrollmentCapacityDecision {
663 EnrollmentCapacityDecision::Respond(EnrollmentResponse::receipt_capacity_exceeded(
664 enrollment_envelope(request),
665 scope,
666 counter.limit(),
667 counter.occupied(),
668 ))
669}
670
671const fn credential_attach_receipt_refusal(
672 request: &CredentialAttachRequest,
673 scope: ReceiptCapacityScope,
674 counter: CapacityCounter,
675) -> CredentialAttachCapacityDecision {
676 CredentialAttachCapacityDecision::Respond(CredentialAttachResponse::receipt_capacity_exceeded(
677 attach_envelope(request),
678 scope,
679 counter.limit(),
680 counter.occupied(),
681 ))
682}
683
684const fn enrollment_envelope(request: &EnrollmentRequest) -> EnrollmentEnvelope {
685 EnrollmentEnvelope {
686 conversation_id: request.conversation_id,
687 enrollment_token: request.enrollment_token,
688 }
689}
690
691const fn attach_envelope(request: &CredentialAttachRequest) -> AttachEnvelope {
692 AttachEnvelope {
693 conversation_id: request.conversation_id,
694 participant_id: request.participant_id,
695 capability_generation: request.capability_generation,
696 attach_attempt_token: request.attach_attempt_token,
697 accept_marker_delivery_seq: request.accept_marker_delivery_seq,
698 }
699}