liminal-protocol 0.2.1

Shared participant-lifecycle protocol types for liminal
Documentation
#![allow(clippy::expect_used, clippy::panic, clippy::too_many_lines)]

use crate::algebra::WideResourceVector;
use crate::wire::{
    AckNoOp, AttachAttemptToken, AttachMarkerProof, AttachSecret, BindingEpoch,
    ConnectionIncarnation, CredentialAttachRequest, Generation, MarkerAck, MarkerAckEnvelope,
    MarkerAckProof, MarkerMismatch, MarkerMismatchBody, MarkerNotDelivered,
    MarkerNotDeliveredReason, MarkerProofRequest,
};

use super::super::edge::{
    ClosureDebt, ClosureState, Event, ParticipantCursorProgress, StoredEdge,
    marker_delivery_for_test,
};
use super::marker_proof::{
    MarkerProofDecision, MarkerProofInput, MarkerProofState, select_marker_proof,
};

const CONVERSATION_ID: u64 = 34;
const PARTICIPANT_ID: u64 = 34;

fn generation(value: u64) -> Generation {
    Generation::new(value).expect("test generation is nonzero")
}

fn epoch(generation: u64, ordinal: u64) -> BindingEpoch {
    BindingEpoch::new(
        ConnectionIncarnation::new(34, ordinal),
        self::generation(generation),
    )
}

fn marker_ack(marker_delivery_seq: u64) -> MarkerAck {
    MarkerAck {
        conversation_id: CONVERSATION_ID,
        participant_id: PARTICIPANT_ID,
        capability_generation: generation(7),
        marker_delivery_seq,
    }
}

fn attach(marker_delivery_seq: Option<u64>) -> CredentialAttachRequest {
    CredentialAttachRequest {
        conversation_id: CONVERSATION_ID,
        participant_id: PARTICIPANT_ID,
        capability_generation: generation(7),
        attach_secret: AttachSecret::new([0x34; 32]),
        attach_attempt_token: AttachAttemptToken::new([0xA3; 16]),
        accept_marker_delivery_seq: marker_delivery_seq,
    }
}

fn ack_proof(marker_delivery_seq: u64) -> MarkerProofRequest {
    MarkerProofRequest::MarkerAck(MarkerAckProof {
        conversation_id: CONVERSATION_ID,
        participant_id: PARTICIPANT_ID,
        capability_generation: generation(7),
        requested_marker_delivery_seq: marker_delivery_seq,
    })
}

fn proof_state(
    current_cursor: u64,
    accepted_marker_at_cursor: bool,
    expected_marker_delivery_seq: Option<u64>,
    progress: Option<ParticipantCursorProgress>,
) -> MarkerProofState {
    MarkerProofState::new(
        current_cursor,
        accepted_marker_at_cursor,
        expected_marker_delivery_seq,
        epoch(7, 1),
        progress,
    )
}

fn marker_progress(
    participant_id: u64,
    binding_epoch: BindingEpoch,
    marker_delivery_seq: u64,
) -> ParticipantCursorProgress {
    let debt = ClosureDebt::new(WideResourceVector::new(1, 1))
        .expect("marker delivery fixture has nonzero closure debt");
    let delivery = marker_delivery_for_test(participant_id, binding_epoch, marker_delivery_seq)
        .expect("validated marker fixture restores");
    let state = delivery
        .delivered(
            debt,
            Event::marker_delivered(participant_id, binding_epoch, marker_delivery_seq),
        )
        .expect("exact final-emitter delivery commits");
    let ClosureState::Owed {
        edge: StoredEdge::ParticipantCursorProgress(progress),
        ..
    } = state
    else {
        panic!("delivery must derive marker-backed cursor progress")
    };
    progress
}

#[test]
fn credential_attach_input_requires_some_marker_and_preserves_its_distinct_envelope() {
    assert_eq!(MarkerProofInput::credential_attach(&attach(None)), None);

    let input = MarkerProofInput::credential_attach(&attach(Some(20)))
        .expect("an explicit attach marker enters marker proof");
    assert_eq!(
        input,
        MarkerProofInput::CredentialAttach(AttachMarkerProof {
            conversation_id: CONVERSATION_ID,
            token: AttachAttemptToken::new([0xA3; 16]),
            participant_id: PARTICIPANT_ID,
            capability_generation: generation(7),
            requested_marker_delivery_seq: 20,
        })
    );

    let no_anchor = proof_state(10, false, None, None);
    assert_eq!(
        select_marker_proof(&no_anchor, input),
        MarkerProofDecision::MarkerMismatch(MarkerMismatch {
            request: MarkerProofRequest::CredentialAttach(AttachMarkerProof {
                conversation_id: CONVERSATION_ID,
                token: AttachAttemptToken::new([0xA3; 16]),
                participant_id: PARTICIPANT_ID,
                capability_generation: generation(7),
                requested_marker_delivery_seq: 20,
            }),
            mismatch: MarkerMismatchBody::NoMarkerExpected,
        })
    );
}

#[test]
fn case_34_marker_ack_matrix_uses_the_frozen_total_precedence() {
    let anchored_undelivered = proof_state(10, false, Some(20), None);

    assert_eq!(
        select_marker_proof(
            &anchored_undelivered,
            MarkerProofInput::marker_ack(&marker_ack(9)),
        ),
        MarkerProofDecision::MarkerMismatch(MarkerMismatch {
            request: ack_proof(9),
            mismatch: MarkerMismatchBody::BelowCursor { current_cursor: 10 },
        })
    );
    assert_eq!(
        select_marker_proof(
            &anchored_undelivered,
            MarkerProofInput::marker_ack(&marker_ack(10)),
        ),
        MarkerProofDecision::MarkerMismatch(MarkerMismatch {
            request: ack_proof(10),
            mismatch: MarkerMismatchBody::ExpectedDifferentMarker {
                expected_marker_delivery_seq: 20,
            },
        }),
        "cursor equality is not a marker replay without an accepted marker record"
    );
    assert_eq!(
        select_marker_proof(
            &anchored_undelivered,
            MarkerProofInput::marker_ack(&marker_ack(19)),
        ),
        MarkerProofDecision::MarkerMismatch(MarkerMismatch {
            request: ack_proof(19),
            mismatch: MarkerMismatchBody::ExpectedDifferentMarker {
                expected_marker_delivery_seq: 20,
            },
        })
    );
    assert_eq!(
        select_marker_proof(
            &anchored_undelivered,
            MarkerProofInput::marker_ack(&marker_ack(20)),
        ),
        MarkerProofDecision::MarkerNotDelivered(MarkerNotDelivered {
            request: ack_proof(20),
            reason: MarkerNotDeliveredReason::NotDeliveredToProofEpoch,
            expected_marker_delivery_seq: 20,
        })
    );

    let no_anchor = proof_state(10, false, None, None);
    assert_eq!(
        select_marker_proof(&no_anchor, MarkerProofInput::marker_ack(&marker_ack(20)),),
        MarkerProofDecision::MarkerMismatch(MarkerMismatch {
            request: ack_proof(20),
            mismatch: MarkerMismatchBody::NoMarkerExpected,
        })
    );

    let accepted = proof_state(
        20,
        true,
        None,
        Some(marker_progress(PARTICIPANT_ID, epoch(7, 1), 20)),
    );
    assert_eq!(
        select_marker_proof(&accepted, MarkerProofInput::marker_ack(&marker_ack(20)),),
        MarkerProofDecision::AckNoOp(AckNoOp::marker_ack(MarkerAckEnvelope {
            conversation_id: CONVERSATION_ID,
            participant_id: PARTICIPANT_ID,
            capability_generation: generation(7),
            marker_delivery_seq: 20,
        }))
    );
    assert_eq!(
        select_marker_proof(&accepted, MarkerProofInput::marker_ack(&marker_ack(19)),),
        MarkerProofDecision::MarkerMismatch(MarkerMismatch {
            request: ack_proof(19),
            mismatch: MarkerMismatchBody::BelowCursor { current_cursor: 20 },
        })
    );

    let accepted_attach = MarkerProofInput::credential_attach(&attach(Some(20)))
        .expect("attach carries an explicit marker");
    assert!(matches!(
        select_marker_proof(&accepted, accepted_attach),
        MarkerProofDecision::MarkerMismatch(MarkerMismatch {
            request: MarkerProofRequest::CredentialAttach(_),
            mismatch: MarkerMismatchBody::NoMarkerExpected,
        })
    ));
}

#[test]
fn exact_delivery_mints_an_opaque_operation_bound_permit() {
    let proof_epoch = epoch(7, 1);
    let progress = marker_progress(PARTICIPANT_ID, proof_epoch, 20);
    let state = proof_state(10, false, Some(20), Some(progress));

    let ack_input = MarkerProofInput::marker_ack(&marker_ack(20));
    let MarkerProofDecision::Permit(ack_permit) = select_marker_proof(&state, ack_input.clone())
    else {
        panic!("exact marker ack delivery must produce a permit")
    };
    assert_eq!(ack_permit.operation(), &ack_input);
    assert_eq!(ack_permit.expected_marker_delivery_seq(), 20);
    assert_eq!(ack_permit.proof_binding_epoch(), proof_epoch);
    assert_eq!(ack_permit.progress(), progress);

    let attach_input = MarkerProofInput::credential_attach(&attach(Some(20)))
        .expect("attach carries an explicit marker");
    let MarkerProofDecision::Permit(attach_permit) =
        select_marker_proof(&state, attach_input.clone())
    else {
        panic!("exact attach delivery must produce a permit")
    };
    assert_eq!(attach_permit.operation(), &attach_input);
    assert_ne!(attach_permit.operation(), ack_permit.operation());
    assert_eq!(attach_permit.progress(), progress);
}

#[test]
fn wrong_participant_epoch_marker_or_generation_never_counts_as_exact_delivery() {
    let request = MarkerProofInput::marker_ack(&marker_ack(20));
    let expected = MarkerProofDecision::MarkerNotDelivered(MarkerNotDelivered {
        request: ack_proof(20),
        reason: MarkerNotDeliveredReason::NotDeliveredToProofEpoch,
        expected_marker_delivery_seq: 20,
    });
    let wrong_progress = [
        marker_progress(PARTICIPANT_ID + 1, epoch(7, 1), 20),
        marker_progress(PARTICIPANT_ID, epoch(7, 2), 20),
        marker_progress(PARTICIPANT_ID, epoch(7, 1), 21),
    ];
    for progress in wrong_progress {
        let state = proof_state(10, false, Some(20), Some(progress));
        assert_eq!(select_marker_proof(&state, request.clone()), expected);
    }

    let continuous = ParticipantCursorProgress::continuous(PARTICIPANT_ID, epoch(7, 1), 20);
    assert_eq!(
        select_marker_proof(
            &proof_state(10, false, Some(20), Some(continuous)),
            request.clone(),
        ),
        expected,
        "continuous PCP has no delivered-marker provenance"
    );

    let wrong_proof_generation = MarkerProofState::new(
        10,
        false,
        Some(20),
        epoch(8, 1),
        Some(marker_progress(PARTICIPANT_ID, epoch(8, 1), 20)),
    );
    assert_eq!(
        select_marker_proof(&wrong_proof_generation, request),
        expected
    );
}

#[test]
fn selection_is_nonmutating_and_crash_replay_is_identical() {
    let state = proof_state(
        10,
        false,
        Some(20),
        Some(marker_progress(PARTICIPANT_ID, epoch(7, 1), 20)),
    );
    let before = state;
    let input = MarkerProofInput::marker_ack(&marker_ack(20));
    let first = select_marker_proof(&state, input.clone());
    let replay = select_marker_proof(&state, input);

    assert_eq!(state, before);
    assert_eq!(replay, first);
    assert!(matches!(first, MarkerProofDecision::Permit(_)));
}