use crate::wire::{
AttachBound, AttachEnvelope, AttemptConflict, AttemptTokenBodyConflict, BindingEpoch,
BindingRequiredEnvelope, BindingStateView, CommonStaleAuthorityEnvelope,
CredentialAttachRequest, DeliverySeq, DetachCommitted, DetachEnvelope, DetachInProgress,
DetachRequest, DetachStaleAuthority, EnrollBound, EnrollmentEnvelope, EnrollmentKnown,
EnrollmentRequest, Generation, LeaveCommitted, LeaveEnvelope, LeaveRequest,
LeaveStaleAuthority, MarkerAck, MarkerAckEnvelope, NoBinding, ParticipantAck,
ParticipantAckEnvelope, ParticipantReferenceEnvelope, ParticipantUnknown, ReceiptExpired,
ReceiptExpiryReason, ReceiptReplay, RecordAdmission, RecordAdmissionEnvelope, Retired,
StaleAuthority, StaleOrUnknownReceipt,
};
use super::{
ActiveBinding, BindingState, DetachCell, IdentityState, LiveMember, PendingReplayRequest,
RetiredIdentity,
};
#[derive(Debug)]
pub enum PresentedIdentity<'a, EF, V, LF> {
Absent,
Live(&'a LiveMember<EF>),
Retired(&'a RetiredIdentity<EF, V, LF>),
}
impl<EF, V, LF> Copy for PresentedIdentity<'_, EF, V, LF> {}
impl<EF, V, LF> Clone for PresentedIdentity<'_, EF, V, LF> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, EF, V, LF> From<Option<&'a IdentityState<EF, V, LF>>>
for PresentedIdentity<'a, EF, V, LF>
{
fn from(value: Option<&'a IdentityState<EF, V, LF>>) -> Self {
match value {
None => Self::Absent,
Some(IdentityState::Live(member)) => Self::Live(member),
Some(IdentityState::Retired(tombstone)) => Self::Retired(tombstone),
}
}
}
#[derive(Debug)]
pub enum ResolvedIdentity<'a, EF, V, LF> {
Live(&'a LiveMember<EF>),
Retired(&'a RetiredIdentity<EF, V, LF>),
}
impl<EF, V, LF> Copy for ResolvedIdentity<'_, EF, V, LF> {}
impl<EF, V, LF> Clone for ResolvedIdentity<'_, EF, V, LF> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, EF, V, LF> From<&'a IdentityState<EF, V, LF>> for ResolvedIdentity<'a, EF, V, LF> {
fn from(value: &'a IdentityState<EF, V, LF>) -> Self {
match value {
IdentityState::Live(member) => Self::Live(member),
IdentityState::Retired(tombstone) => Self::Retired(tombstone),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AttachSecretProof {
Mismatch,
Verified,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EnrollmentLiveReceipt {
committed: EnrollBound,
}
impl EnrollmentLiveReceipt {
#[must_use]
pub const fn from_commit(committed: EnrollBound) -> Self {
Self { committed }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EnrollmentProvenance {
result_generation: Generation,
reason: ReceiptExpiryReason,
}
impl EnrollmentProvenance {
#[must_use]
pub const fn new(result_generation: Generation, reason: ReceiptExpiryReason) -> Self {
Self {
result_generation,
reason,
}
}
}
#[derive(Debug)]
pub enum EnrollmentTokenPhase<'a, EF, V, LF> {
Unmapped,
LiveReceipt {
identity: ResolvedIdentity<'a, EF, V, LF>,
receipt: &'a EnrollmentLiveReceipt,
},
Provenance {
identity: ResolvedIdentity<'a, EF, V, LF>,
provenance: EnrollmentProvenance,
},
LifetimeMapping {
identity: ResolvedIdentity<'a, EF, V, LF>,
},
}
impl<EF, V, LF> Copy for EnrollmentTokenPhase<'_, EF, V, LF> {}
impl<EF, V, LF> Clone for EnrollmentTokenPhase<'_, EF, V, LF> {
fn clone(&self) -> Self {
*self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EnrollmentLookupResult {
Retired(Retired),
Bound(ReceiptReplay),
UnboundReceipt(ReceiptReplay),
ReceiptExpired(ReceiptExpired),
EnrollmentKnown(EnrollmentKnown),
AuthorizedNew,
}
#[must_use]
pub fn lookup_enrollment<EF, V, LF>(
phase: EnrollmentTokenPhase<'_, EF, V, LF>,
binding: &BindingState,
request: &EnrollmentRequest,
) -> EnrollmentLookupResult {
match phase {
EnrollmentTokenPhase::Unmapped => EnrollmentLookupResult::AuthorizedNew,
EnrollmentTokenPhase::LiveReceipt { identity, receipt } => match identity {
ResolvedIdentity::Retired(tombstone) => {
EnrollmentLookupResult::Retired(retired_enrollment(tombstone, request))
}
ResolvedIdentity::Live(member) => {
let committed = &receipt.committed;
let replay = ReceiptReplay::Enrollment(committed.clone());
if receipt_binding_is_current(
binding,
request.conversation_id,
member.participant_id(),
committed.origin_binding_epoch(),
) {
EnrollmentLookupResult::Bound(replay)
} else {
EnrollmentLookupResult::UnboundReceipt(replay)
}
}
},
EnrollmentTokenPhase::Provenance {
identity,
provenance,
} => match identity {
ResolvedIdentity::Retired(tombstone) => {
EnrollmentLookupResult::Retired(retired_enrollment(tombstone, request))
}
ResolvedIdentity::Live(member) => {
EnrollmentLookupResult::ReceiptExpired(ReceiptExpired::Enrollment {
conversation_id: request.conversation_id,
token: request.enrollment_token,
participant_id: member.participant_id(),
result_generation: provenance.result_generation,
current_generation: member.generation(),
reason: provenance.reason,
})
}
},
EnrollmentTokenPhase::LifetimeMapping { identity } => match identity {
ResolvedIdentity::Retired(tombstone) => {
EnrollmentLookupResult::Retired(retired_enrollment(tombstone, request))
}
ResolvedIdentity::Live(member) => {
EnrollmentLookupResult::EnrollmentKnown(EnrollmentKnown {
conversation_id: request.conversation_id,
token: request.enrollment_token,
participant_id: member.participant_id(),
current_generation: member.generation(),
})
}
},
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CredentialAttachLiveReceipt {
committed: AttachBound,
}
impl CredentialAttachLiveReceipt {
#[must_use]
pub const fn from_commit(committed: AttachBound) -> Self {
Self { committed }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CredentialAttachProvenance {
result_generation: Generation,
reason: ReceiptExpiryReason,
}
impl CredentialAttachProvenance {
#[must_use]
pub const fn new(result_generation: Generation, reason: ReceiptExpiryReason) -> Self {
Self {
result_generation,
reason,
}
}
}
#[derive(Debug)]
pub enum CredentialAttachTokenPhase<'a, EF, V, LF> {
NoMatch,
LiveReceipt {
identity: ResolvedIdentity<'a, EF, V, LF>,
receipt: &'a CredentialAttachLiveReceipt,
},
Provenance {
identity: ResolvedIdentity<'a, EF, V, LF>,
provenance: CredentialAttachProvenance,
},
AfterProvenance,
}
impl<EF, V, LF> Copy for CredentialAttachTokenPhase<'_, EF, V, LF> {}
impl<EF, V, LF> Clone for CredentialAttachTokenPhase<'_, EF, V, LF> {
fn clone(&self) -> Self {
*self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CredentialAttachLookupResult<'a, EF> {
Retired(Retired),
ParticipantUnknown(ParticipantUnknown),
StaleAuthority(StaleAuthority),
AttemptTokenBodyConflict(AttemptTokenBodyConflict),
Bound(ReceiptReplay),
UnboundReceipt(ReceiptReplay),
ReceiptExpired(ReceiptExpired),
StaleOrUnknownReceipt(StaleOrUnknownReceipt),
AuthorizedFresh {
member: &'a LiveMember<EF>,
},
}
#[must_use]
pub fn lookup_credential_attach<'a, EF, V, LF>(
token_phase: CredentialAttachTokenPhase<'a, EF, V, LF>,
presented_identity: PresentedIdentity<'a, EF, V, LF>,
binding: &BindingState,
request: &CredentialAttachRequest,
secret_proof: AttachSecretProof,
) -> CredentialAttachLookupResult<'a, EF> {
match token_phase {
CredentialAttachTokenPhase::LiveReceipt { identity, receipt } => {
return match identity {
ResolvedIdentity::Retired(tombstone) => {
CredentialAttachLookupResult::Retired(retired_attach(tombstone, request))
}
ResolvedIdentity::Live(member) => {
lookup_live_attach_receipt(member, binding, request, secret_proof, receipt)
}
};
}
CredentialAttachTokenPhase::Provenance {
identity,
provenance,
} => {
return match identity {
ResolvedIdentity::Retired(tombstone) => {
CredentialAttachLookupResult::Retired(retired_attach(tombstone, request))
}
ResolvedIdentity::Live(member) => {
CredentialAttachLookupResult::ReceiptExpired(ReceiptExpired::CredentialAttach {
conversation_id: request.conversation_id,
token: request.attach_attempt_token,
participant_id: request.participant_id,
presented_generation: request.capability_generation,
presented_marker_delivery_seq: request.accept_marker_delivery_seq,
result_generation: provenance.result_generation,
current_generation: member.generation(),
reason: provenance.reason,
})
}
};
}
CredentialAttachTokenPhase::NoMatch | CredentialAttachTokenPhase::AfterProvenance => {}
}
let member = match presented_identity {
PresentedIdentity::Retired(tombstone) => {
return CredentialAttachLookupResult::Retired(retired_attach(tombstone, request));
}
PresentedIdentity::Absent => {
return CredentialAttachLookupResult::ParticipantUnknown(unknown_attach(request));
}
PresentedIdentity::Live(member) => {
if member.conversation_id() != request.conversation_id
|| member.participant_id() != request.participant_id
{
return CredentialAttachLookupResult::ParticipantUnknown(unknown_attach(request));
}
member
}
};
if matches!(token_phase, CredentialAttachTokenPhase::AfterProvenance) {
return CredentialAttachLookupResult::StaleOrUnknownReceipt(StaleOrUnknownReceipt {
conversation_id: request.conversation_id,
token: request.attach_attempt_token,
participant_id: request.participant_id,
presented_generation: request.capability_generation,
presented_marker_delivery_seq: request.accept_marker_delivery_seq,
current_generation: member.generation(),
});
}
if request.capability_generation != member.generation()
|| secret_proof == AttachSecretProof::Mismatch
{
return CredentialAttachLookupResult::StaleAuthority(stale_attach(
request,
member.generation(),
));
}
CredentialAttachLookupResult::AuthorizedFresh { member }
}
fn lookup_live_attach_receipt<'a, EF>(
member: &'a LiveMember<EF>,
binding: &BindingState,
request: &CredentialAttachRequest,
secret_proof: AttachSecretProof,
receipt: &CredentialAttachLiveReceipt,
) -> CredentialAttachLookupResult<'a, EF> {
if secret_proof == AttachSecretProof::Mismatch {
return CredentialAttachLookupResult::StaleAuthority(stale_attach(
request,
member.generation(),
));
}
let committed = &receipt.committed;
let conflict = if request.capability_generation != committed.request_generation() {
Some(AttemptConflict::Generation)
} else if request.accept_marker_delivery_seq != committed.accepted_marker_delivery_seq() {
Some(AttemptConflict::MarkerDeliverySequence)
} else {
None
};
if let Some(conflict) = conflict {
return CredentialAttachLookupResult::AttemptTokenBodyConflict(
AttemptTokenBodyConflict::CredentialAttach {
token: request.attach_attempt_token,
conversation_id: request.conversation_id,
presented_participant_id: request.participant_id,
presented_generation: request.capability_generation,
presented_marker_delivery_seq: request.accept_marker_delivery_seq,
conflict,
},
);
}
let replay = ReceiptReplay::CredentialAttach(committed.clone());
if receipt_binding_is_current(
binding,
request.conversation_id,
request.participant_id,
committed.origin_binding_epoch(),
) {
CredentialAttachLookupResult::Bound(replay)
} else {
CredentialAttachLookupResult::UnboundReceipt(replay)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ParticipantBindingRequest {
ParticipantAck(ParticipantAck),
MarkerAck(MarkerAck),
RecordAdmission(RecordAdmission),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BindingRequiredLookupResult<'a, EF> {
Retired(Retired),
ParticipantUnknown(ParticipantUnknown),
StaleAuthority(StaleAuthority),
NoBinding(NoBinding),
Authorized {
member: &'a LiveMember<EF>,
binding: ActiveBinding,
},
}
#[must_use]
pub fn lookup_binding_required<'a, EF, V, LF>(
identity: PresentedIdentity<'a, EF, V, LF>,
binding: &BindingState,
receiving_binding_epoch: Option<BindingEpoch>,
request: &ParticipantBindingRequest,
) -> BindingRequiredLookupResult<'a, EF> {
let member = match identity {
PresentedIdentity::Retired(tombstone) => {
return BindingRequiredLookupResult::Retired(Retired::Participant {
request: participant_reference(request),
retired_generation: tombstone.retired_generation(),
});
}
PresentedIdentity::Absent => {
return BindingRequiredLookupResult::ParticipantUnknown(ParticipantUnknown {
request: participant_reference(request),
});
}
PresentedIdentity::Live(member) => {
if member.conversation_id() != binding_request_conversation(request)
|| member.participant_id() != binding_request_participant(request)
{
return BindingRequiredLookupResult::ParticipantUnknown(ParticipantUnknown {
request: participant_reference(request),
});
}
member
}
};
if binding_request_generation(request) != member.generation() {
return BindingRequiredLookupResult::StaleAuthority(StaleAuthority::Live {
request: common_stale_envelope(request),
current_generation: member.generation(),
});
}
match binding {
BindingState::Bound(active)
if active.conversation_id == member.conversation_id()
&& active.participant_id == member.participant_id()
&& active.binding_epoch.capability_generation == member.generation()
&& receiving_binding_epoch == Some(active.binding_epoch) =>
{
BindingRequiredLookupResult::Authorized {
member,
binding: *active,
}
}
BindingState::Detached | BindingState::PendingFinalization(_) | BindingState::Bound(_) => {
BindingRequiredLookupResult::NoBinding(NoBinding {
request: binding_required_envelope(request),
})
}
}
}
#[derive(Debug)]
pub enum DetachTokenResolution<'a, EF, V, LF> {
NoExactMatch,
Exact(ResolvedIdentity<'a, EF, V, LF>),
}
impl<EF, V, LF> Copy for DetachTokenResolution<'_, EF, V, LF> {}
impl<EF, V, LF> Clone for DetachTokenResolution<'_, EF, V, LF> {
fn clone(&self) -> Self {
*self
}
}
pub struct DetachLookupContext<'a, EF, V, LF, D> {
pub token_resolution: DetachTokenResolution<'a, EF, V, LF>,
pub presented_identity: PresentedIdentity<'a, EF, V, LF>,
pub cell: &'a DetachCell<D>,
pub binding: &'a BindingState,
pub receiving_binding_epoch: Option<BindingEpoch>,
pub request: &'a DetachRequest,
pub request_verifier: D,
pub observer_progress: DeliverySeq,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DetachLookupResult<'a, EF, D> {
Retired(Retired),
ParticipantUnknown(ParticipantUnknown),
StaleAuthority(DetachStaleAuthority),
NoBinding(NoBinding),
PendingReplayRequired(PendingReplayRequest<D>),
DetachInProgress(DetachInProgress),
DetachCommitted(DetachCommitted),
Authorized {
member: &'a LiveMember<EF>,
binding: ActiveBinding,
},
}
#[must_use]
pub fn lookup_detach<'a, EF, V, LF, D>(
context: &DetachLookupContext<'a, EF, V, LF, D>,
) -> DetachLookupResult<'a, EF, D>
where
D: Copy + Eq,
{
let token_resolution = context.token_resolution;
let presented_identity = context.presented_identity;
let cell = context.cell;
let binding = context.binding;
let receiving_binding_epoch = context.receiving_binding_epoch;
let request = context.request;
let request_verifier = context.request_verifier;
let observer_progress = context.observer_progress;
let exact_member = match token_resolution {
DetachTokenResolution::Exact(ResolvedIdentity::Retired(tombstone)) => {
return DetachLookupResult::Retired(retired_detach(tombstone, request));
}
DetachTokenResolution::Exact(ResolvedIdentity::Live(member)) => Some(member),
DetachTokenResolution::NoExactMatch => None,
};
if let Some(member) = exact_member
&& let Some(result) = lookup_exact_detach_cell(
member,
cell,
binding,
request,
request_verifier,
observer_progress,
)
{
return result;
}
let member = match presented_identity {
PresentedIdentity::Retired(tombstone) => {
return DetachLookupResult::Retired(retired_detach(tombstone, request));
}
PresentedIdentity::Absent => {
return DetachLookupResult::ParticipantUnknown(unknown_detach(request));
}
PresentedIdentity::Live(member) => {
if member.conversation_id() != request.conversation_id
|| member.participant_id() != request.participant_id
{
return DetachLookupResult::ParticipantUnknown(unknown_detach(request));
}
member
}
};
if matches!(token_resolution, DetachTokenResolution::NoExactMatch)
&& let DetachCell::Pending(pending) = cell
{
return DetachLookupResult::DetachInProgress(pending.competing_attempt(
request.conversation_id,
request.detach_attempt_token,
request.capability_generation,
));
}
if request.capability_generation != member.generation() {
return stale_detach(request, member.generation());
}
match binding {
BindingState::Bound(active)
if active.conversation_id == request.conversation_id
&& active.participant_id == request.participant_id
&& active.binding_epoch.capability_generation == request.capability_generation
&& receiving_binding_epoch == Some(active.binding_epoch) =>
{
DetachLookupResult::Authorized {
member,
binding: *active,
}
}
BindingState::Detached | BindingState::PendingFinalization(_) | BindingState::Bound(_) => {
DetachLookupResult::NoBinding(no_binding_detach(request))
}
}
}
fn lookup_exact_detach_cell<'a, EF, D>(
member: &'a LiveMember<EF>,
cell: &DetachCell<D>,
binding: &BindingState,
request: &DetachRequest,
request_verifier: D,
observer_progress: DeliverySeq,
) -> Option<DetachLookupResult<'a, EF, D>>
where
D: Copy + Eq,
{
if request.conversation_id != member.conversation_id() {
return None;
}
match cell {
DetachCell::Empty(_) => None,
DetachCell::Pending(pending) => {
Some(pending.verify_exact(request, request_verifier).map_or_else(
|_| stale_detach(request, member.generation()),
|verified| {
DetachLookupResult::PendingReplayRequired(verified.prepare_replay(
request.conversation_id,
*binding,
observer_progress,
))
},
))
}
DetachCell::Committed(committed) => Some(
committed
.verify_exact(request, request_verifier)
.map_or_else(
|_| stale_detach(request, member.generation()),
|verified| {
DetachLookupResult::DetachCommitted(
verified.outcome(request.conversation_id),
)
},
),
),
DetachCell::Terminalized(terminalized) => Some(
terminalized
.verify_exact(request, request_verifier)
.map_or_else(
|_| stale_detach(request, member.generation()),
|verified| {
let outcome = verified.outcome(
request.conversation_id,
member.generation(),
binding_view(member, binding),
);
DetachLookupResult::StaleAuthority(
DetachStaleAuthority::TerminalizedDetachCell(outcome),
)
},
),
),
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LeaveSecretProof {
Mismatch,
Verified,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LeaveLookupResult<'a, EF> {
LeaveCommitted(LeaveCommitted),
AttemptTokenBodyConflict(AttemptTokenBodyConflict),
StaleAuthority(LeaveStaleAuthority),
Retired(Retired),
ParticipantUnknown(ParticipantUnknown),
NoBinding(NoBinding),
AuthorizedBound {
member: &'a LiveMember<EF>,
binding: ActiveBinding,
},
AuthorizedDetached {
member: &'a LiveMember<EF>,
},
}
#[must_use]
pub fn lookup_leave<'a, EF, V, LF>(
identity: PresentedIdentity<'a, EF, V, LF>,
binding: &BindingState,
request_binding_epoch: Option<crate::wire::BindingEpoch>,
request: &LeaveRequest,
secret_proof: LeaveSecretProof,
) -> LeaveLookupResult<'a, EF> {
match identity {
PresentedIdentity::Retired(tombstone) => {
lookup_retired_leave(tombstone, request, secret_proof)
}
PresentedIdentity::Absent => LeaveLookupResult::ParticipantUnknown(unknown_leave(request)),
PresentedIdentity::Live(member) => {
if member.conversation_id() != request.conversation_id
|| member.participant_id() != request.participant_id
{
return LeaveLookupResult::ParticipantUnknown(unknown_leave(request));
}
let generation_mismatch = request.capability_generation != member.generation();
let secret_mismatch = secret_proof == LeaveSecretProof::Mismatch;
if generation_mismatch || secret_mismatch {
return LeaveLookupResult::StaleAuthority(live_leave_stale(
request,
member.generation(),
));
}
match binding {
BindingState::Bound(active)
if active.conversation_id == request.conversation_id
&& active.participant_id == request.participant_id
&& request_binding_epoch == Some(active.binding_epoch) =>
{
LeaveLookupResult::AuthorizedBound {
member,
binding: *active,
}
}
BindingState::Bound(_) => LeaveLookupResult::NoBinding(no_binding_leave(request)),
BindingState::Detached | BindingState::PendingFinalization(_) => {
LeaveLookupResult::AuthorizedDetached { member }
}
}
}
}
}
fn lookup_retired_leave<'a, EF, V, LF>(
tombstone: &RetiredIdentity<EF, V, LF>,
request: &LeaveRequest,
secret_proof: LeaveSecretProof,
) -> LeaveLookupResult<'a, EF> {
if request.conversation_id != tombstone.conversation_id()
|| request.participant_id != tombstone.participant_id()
{
return LeaveLookupResult::ParticipantUnknown(unknown_leave(request));
}
if request.leave_attempt_token != tombstone.leave_attempt_token() {
return LeaveLookupResult::Retired(retired_leave(tombstone, request));
}
if secret_proof == LeaveSecretProof::Mismatch {
return LeaveLookupResult::StaleAuthority(LeaveStaleAuthority::CommittedLeaveTombstone {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
presented_generation: request.capability_generation,
leave_attempt_token: request.leave_attempt_token,
retired_generation: tombstone.retired_generation(),
});
}
if request.capability_generation != tombstone.committed_result().presented_generation() {
return LeaveLookupResult::AttemptTokenBodyConflict(AttemptTokenBodyConflict::Leave {
token: request.leave_attempt_token,
conversation_id: request.conversation_id,
presented_participant_id: request.participant_id,
presented_generation: request.capability_generation,
});
}
LeaveLookupResult::LeaveCommitted(tombstone.committed_result().clone())
}
fn receipt_binding_is_current(
binding: &BindingState,
conversation_id: crate::wire::ConversationId,
participant_id: crate::wire::ParticipantId,
origin_binding_epoch: BindingEpoch,
) -> bool {
match binding {
BindingState::Bound(active) => {
active.conversation_id == conversation_id
&& active.participant_id == participant_id
&& active.binding_epoch == origin_binding_epoch
}
BindingState::Detached | BindingState::PendingFinalization(_) => false,
}
}
const fn enrollment_envelope(request: &EnrollmentRequest) -> EnrollmentEnvelope {
EnrollmentEnvelope {
conversation_id: request.conversation_id,
enrollment_token: request.enrollment_token,
}
}
const fn attach_envelope(request: &CredentialAttachRequest) -> AttachEnvelope {
AttachEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
attach_attempt_token: request.attach_attempt_token,
accept_marker_delivery_seq: request.accept_marker_delivery_seq,
}
}
const fn retired_enrollment<EF, V, LF>(
tombstone: &RetiredIdentity<EF, V, LF>,
request: &EnrollmentRequest,
) -> Retired {
Retired::Enrollment {
request: enrollment_envelope(request),
participant_id: tombstone.participant_id(),
retired_generation: tombstone.retired_generation(),
}
}
const fn retired_attach<EF, V, LF>(
tombstone: &RetiredIdentity<EF, V, LF>,
request: &CredentialAttachRequest,
) -> Retired {
Retired::Participant {
request: ParticipantReferenceEnvelope::CredentialAttach(attach_envelope(request)),
retired_generation: tombstone.retired_generation(),
}
}
const fn unknown_attach(request: &CredentialAttachRequest) -> ParticipantUnknown {
ParticipantUnknown {
request: ParticipantReferenceEnvelope::CredentialAttach(attach_envelope(request)),
}
}
const fn stale_attach(
request: &CredentialAttachRequest,
current_generation: Generation,
) -> StaleAuthority {
StaleAuthority::Live {
request: CommonStaleAuthorityEnvelope::CredentialAttach(attach_envelope(request)),
current_generation,
}
}
const fn binding_request_conversation(
request: &ParticipantBindingRequest,
) -> crate::wire::ConversationId {
match request {
ParticipantBindingRequest::ParticipantAck(request) => request.conversation_id,
ParticipantBindingRequest::MarkerAck(request) => request.conversation_id,
ParticipantBindingRequest::RecordAdmission(request) => request.conversation_id,
}
}
const fn binding_request_participant(
request: &ParticipantBindingRequest,
) -> crate::wire::ParticipantId {
match request {
ParticipantBindingRequest::ParticipantAck(request) => request.participant_id,
ParticipantBindingRequest::MarkerAck(request) => request.participant_id,
ParticipantBindingRequest::RecordAdmission(request) => request.participant_id,
}
}
const fn binding_request_generation(request: &ParticipantBindingRequest) -> Generation {
match request {
ParticipantBindingRequest::ParticipantAck(request) => request.capability_generation,
ParticipantBindingRequest::MarkerAck(request) => request.capability_generation,
ParticipantBindingRequest::RecordAdmission(request) => request.capability_generation,
}
}
const fn participant_ack_envelope(request: &ParticipantAck) -> ParticipantAckEnvelope {
ParticipantAckEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
through_seq: request.through_seq,
}
}
const fn marker_ack_envelope(request: &MarkerAck) -> MarkerAckEnvelope {
MarkerAckEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
marker_delivery_seq: request.marker_delivery_seq,
}
}
const fn record_admission_envelope(request: &RecordAdmission) -> RecordAdmissionEnvelope {
RecordAdmissionEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
record_admission_attempt_token: request.record_admission_attempt_token,
}
}
const fn participant_reference(
request: &ParticipantBindingRequest,
) -> ParticipantReferenceEnvelope {
match request {
ParticipantBindingRequest::ParticipantAck(request) => {
ParticipantReferenceEnvelope::ParticipantAck(participant_ack_envelope(request))
}
ParticipantBindingRequest::MarkerAck(request) => {
ParticipantReferenceEnvelope::MarkerAck(marker_ack_envelope(request))
}
ParticipantBindingRequest::RecordAdmission(request) => {
ParticipantReferenceEnvelope::RecordAdmission(record_admission_envelope(request))
}
}
}
const fn binding_required_envelope(request: &ParticipantBindingRequest) -> BindingRequiredEnvelope {
match request {
ParticipantBindingRequest::ParticipantAck(request) => {
BindingRequiredEnvelope::ParticipantAck(participant_ack_envelope(request))
}
ParticipantBindingRequest::MarkerAck(request) => {
BindingRequiredEnvelope::MarkerAck(marker_ack_envelope(request))
}
ParticipantBindingRequest::RecordAdmission(request) => {
BindingRequiredEnvelope::RecordAdmission(record_admission_envelope(request))
}
}
}
const fn common_stale_envelope(
request: &ParticipantBindingRequest,
) -> CommonStaleAuthorityEnvelope {
match request {
ParticipantBindingRequest::ParticipantAck(request) => {
CommonStaleAuthorityEnvelope::ParticipantAck(participant_ack_envelope(request))
}
ParticipantBindingRequest::MarkerAck(request) => {
CommonStaleAuthorityEnvelope::MarkerAck(marker_ack_envelope(request))
}
ParticipantBindingRequest::RecordAdmission(request) => {
CommonStaleAuthorityEnvelope::RecordAdmission(record_admission_envelope(request))
}
}
}
fn binding_view<EF>(member: &LiveMember<EF>, binding: &BindingState) -> BindingStateView {
match binding {
BindingState::Bound(active)
if active.participant_id == member.participant_id()
&& active.conversation_id == member.conversation_id()
&& active.binding_epoch.capability_generation == member.generation() =>
{
BindingStateView::Bound {
current_binding_epoch: active.binding_epoch,
}
}
BindingState::Detached | BindingState::PendingFinalization(_) | BindingState::Bound(_) => {
BindingStateView::Detached
}
}
}
const fn detach_envelope(request: &DetachRequest) -> DetachEnvelope {
DetachEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
detach_attempt_token: request.detach_attempt_token,
}
}
const fn leave_envelope(request: &LeaveRequest) -> LeaveEnvelope {
LeaveEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
leave_attempt_token: request.leave_attempt_token,
}
}
const fn retired_detach<EF, V, LF>(
tombstone: &RetiredIdentity<EF, V, LF>,
request: &DetachRequest,
) -> Retired {
Retired::Participant {
request: ParticipantReferenceEnvelope::Detach(detach_envelope(request)),
retired_generation: tombstone.retired_generation(),
}
}
const fn retired_leave<EF, V, LF>(
tombstone: &RetiredIdentity<EF, V, LF>,
request: &LeaveRequest,
) -> Retired {
Retired::Participant {
request: ParticipantReferenceEnvelope::Leave(leave_envelope(request)),
retired_generation: tombstone.retired_generation(),
}
}
const fn unknown_detach(request: &DetachRequest) -> ParticipantUnknown {
ParticipantUnknown {
request: ParticipantReferenceEnvelope::Detach(detach_envelope(request)),
}
}
const fn unknown_leave(request: &LeaveRequest) -> ParticipantUnknown {
ParticipantUnknown {
request: ParticipantReferenceEnvelope::Leave(leave_envelope(request)),
}
}
const fn no_binding_detach(request: &DetachRequest) -> NoBinding {
NoBinding {
request: BindingRequiredEnvelope::Detach(detach_envelope(request)),
}
}
const fn no_binding_leave(request: &LeaveRequest) -> NoBinding {
NoBinding {
request: BindingRequiredEnvelope::Leave(leave_envelope(request)),
}
}
const fn stale_detach<'a, EF, D>(
request: &DetachRequest,
current_generation: Generation,
) -> DetachLookupResult<'a, EF, D> {
DetachLookupResult::StaleAuthority(DetachStaleAuthority::Live {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
detach_attempt_token: request.detach_attempt_token,
current_generation,
})
}
const fn live_leave_stale(
request: &LeaveRequest,
current_generation: Generation,
) -> LeaveStaleAuthority {
LeaveStaleAuthority::Live {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
presented_generation: request.capability_generation,
leave_attempt_token: request.leave_attempt_token,
current_generation,
}
}