use crate::wire::{
AckNoOp, AttachMarkerProof, BindingEpoch, CredentialAttachRequest, DeliverySeq, MarkerAck,
MarkerAckEnvelope, MarkerAckProof, MarkerMismatch, MarkerMismatchBody, MarkerNotDelivered,
MarkerNotDeliveredReason, MarkerProofRequest, ParticipantId,
};
use super::super::edge::ParticipantCursorProgress;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MarkerProofInput {
CredentialAttach(AttachMarkerProof),
MarkerAck(MarkerAckProof),
}
impl MarkerProofInput {
#[must_use]
pub const fn credential_attach(request: &CredentialAttachRequest) -> Option<Self> {
let Some(requested_marker_delivery_seq) = request.accept_marker_delivery_seq else {
return None;
};
Some(Self::CredentialAttach(AttachMarkerProof {
conversation_id: request.conversation_id,
token: request.attach_attempt_token,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
requested_marker_delivery_seq,
}))
}
#[must_use]
pub const fn marker_ack(request: &MarkerAck) -> Self {
Self::MarkerAck(MarkerAckProof {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
requested_marker_delivery_seq: request.marker_delivery_seq,
})
}
#[must_use]
pub const fn requested_marker_delivery_seq(&self) -> DeliverySeq {
match self {
Self::CredentialAttach(request) => request.requested_marker_delivery_seq,
Self::MarkerAck(request) => request.requested_marker_delivery_seq,
}
}
#[must_use]
pub const fn participant_id(&self) -> ParticipantId {
match self {
Self::CredentialAttach(request) => request.participant_id,
Self::MarkerAck(request) => request.participant_id,
}
}
const fn capability_generation(&self) -> crate::wire::Generation {
match self {
Self::CredentialAttach(request) => request.capability_generation,
Self::MarkerAck(request) => request.capability_generation,
}
}
const fn is_marker_ack(&self) -> bool {
matches!(self, Self::MarkerAck(_))
}
const fn into_wire_request(self) -> MarkerProofRequest {
match self {
Self::CredentialAttach(request) => MarkerProofRequest::CredentialAttach(request),
Self::MarkerAck(request) => MarkerProofRequest::MarkerAck(request),
}
}
const fn marker_ack_envelope(&self) -> Option<MarkerAckEnvelope> {
let Self::MarkerAck(request) = self else {
return None;
};
Some(MarkerAckEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
marker_delivery_seq: request.requested_marker_delivery_seq,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MarkerProofState {
current_cursor: DeliverySeq,
accepted_marker_at_cursor: bool,
expected_marker_delivery_seq: Option<DeliverySeq>,
proof_binding_epoch: BindingEpoch,
delivered_to_proof_epoch: Option<ParticipantCursorProgress>,
}
impl MarkerProofState {
#[must_use]
pub const fn new(
current_cursor: DeliverySeq,
accepted_marker_at_cursor: bool,
expected_marker_delivery_seq: Option<DeliverySeq>,
proof_binding_epoch: BindingEpoch,
delivered_to_proof_epoch: Option<ParticipantCursorProgress>,
) -> Self {
Self {
current_cursor,
accepted_marker_at_cursor,
expected_marker_delivery_seq,
proof_binding_epoch,
delivered_to_proof_epoch,
}
}
#[must_use]
pub const fn current_cursor(self) -> DeliverySeq {
self.current_cursor
}
#[must_use]
pub const fn accepted_marker_at_cursor(self) -> bool {
self.accepted_marker_at_cursor
}
#[must_use]
pub const fn expected_marker_delivery_seq(self) -> Option<DeliverySeq> {
self.expected_marker_delivery_seq
}
#[must_use]
pub const fn proof_binding_epoch(self) -> BindingEpoch {
self.proof_binding_epoch
}
#[must_use]
pub const fn delivered_to_proof_epoch(self) -> Option<ParticipantCursorProgress> {
self.delivered_to_proof_epoch
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MarkerProofPermit {
operation: MarkerProofInput,
expected_marker_delivery_seq: DeliverySeq,
proof_binding_epoch: BindingEpoch,
progress: ParticipantCursorProgress,
}
impl MarkerProofPermit {
#[must_use]
pub const fn operation(&self) -> &MarkerProofInput {
&self.operation
}
#[must_use]
pub const fn expected_marker_delivery_seq(&self) -> DeliverySeq {
self.expected_marker_delivery_seq
}
#[must_use]
pub const fn proof_binding_epoch(&self) -> BindingEpoch {
self.proof_binding_epoch
}
#[must_use]
pub const fn progress(&self) -> ParticipantCursorProgress {
self.progress
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MarkerProofDecision {
AckNoOp(AckNoOp),
MarkerMismatch(MarkerMismatch),
MarkerNotDelivered(MarkerNotDelivered),
Permit(MarkerProofPermit),
}
#[must_use]
pub fn select_marker_proof(
state: &MarkerProofState,
input: MarkerProofInput,
) -> MarkerProofDecision {
let requested = input.requested_marker_delivery_seq();
if requested < state.current_cursor {
return MarkerProofDecision::MarkerMismatch(MarkerMismatch {
request: input.into_wire_request(),
mismatch: MarkerMismatchBody::BelowCursor {
current_cursor: state.current_cursor,
},
});
}
if requested == state.current_cursor && state.accepted_marker_at_cursor && input.is_marker_ack()
{
if let Some(envelope) = input.marker_ack_envelope() {
return MarkerProofDecision::AckNoOp(AckNoOp::marker_ack(envelope));
}
}
let Some(expected) = state.expected_marker_delivery_seq else {
return MarkerProofDecision::MarkerMismatch(MarkerMismatch {
request: input.into_wire_request(),
mismatch: MarkerMismatchBody::NoMarkerExpected,
});
};
if requested != expected {
return MarkerProofDecision::MarkerMismatch(MarkerMismatch {
request: input.into_wire_request(),
mismatch: MarkerMismatchBody::ExpectedDifferentMarker {
expected_marker_delivery_seq: expected,
},
});
}
let Some(progress) = state.delivered_to_proof_epoch else {
return MarkerProofDecision::MarkerNotDelivered(MarkerNotDelivered {
request: input.into_wire_request(),
reason: MarkerNotDeliveredReason::NotDeliveredToProofEpoch,
expected_marker_delivery_seq: expected,
});
};
if progress.participant_id() != input.participant_id()
|| progress.binding_epoch() != state.proof_binding_epoch
|| progress.through_seq() != expected
|| progress.marker_delivery_seq() != Some(expected)
|| state.proof_binding_epoch.capability_generation != input.capability_generation()
{
return MarkerProofDecision::MarkerNotDelivered(MarkerNotDelivered {
request: input.into_wire_request(),
reason: MarkerNotDeliveredReason::NotDeliveredToProofEpoch,
expected_marker_delivery_seq: expected,
});
}
MarkerProofDecision::Permit(MarkerProofPermit {
operation: input,
expected_marker_delivery_seq: expected,
proof_binding_epoch: state.proof_binding_epoch,
progress,
})
}