#![allow(clippy::expect_used, clippy::panic)]
use alloc::{vec, vec::Vec};
use crate::algebra::{ResourceVector, WideResourceVector};
use crate::outcome::CandidatePhase;
use crate::wire::{
AttachSecret, BindingEpoch, ConnectionIncarnation, Generation, RecordAdmission,
RecordAdmissionAttemptToken,
};
use super::{
super::{
ActiveBinding, AdmissionOrder, BindingState, CapacityCounter, ClosureAccounting,
ClosureState, ConnectionConversationTracking, EnrollmentFingerprint, LiveMember,
LiveMemberRestore, OrderClaims, OrderHigh, OrderLedger, PresentedIdentity,
RecoverySequenceReserve, SequenceClaims, SequenceLedger,
claim_frontier::{
BindingTerminalOwner, ClaimFrontiers, ClaimFrontiersRestore, FrontierBinding,
FrontierParticipant, MarkerProvenance, MovableOrderClaim, MovableSequenceClaim,
OrderClaimFrontierRestore, OrderDirectOwner, RetainedCausalRecord,
RetainedCausalRecordKind, SequenceClaimFrontierRestore, SequenceDirectOwner,
SequenceProductRangesRestore, TerminalProductRangeRestore,
},
},
OrdinaryProjectionError, OrdinaryProjectionLimits, RetainedRecordCharge,
live_frontier::{LiveFrontierOwner, MarkerAnchorReconcile},
record_admission::{
RecordAdmissionDecision, RecordAdmissionFault, RecordAdmissionPrestate,
apply_record_admission,
},
};
const CONVERSATION_ID: u64 = 6;
const P0: u64 = 0;
const MARKER_SEQ: u64 = 12;
fn epoch() -> BindingEpoch {
BindingEpoch::new(
ConnectionIncarnation::new(6, 0),
Generation::new(1).expect("test generation is nonzero"),
)
}
fn owner_with(cursor: u64, stored_anchors: u64) -> LiveFrontierOwner {
owner_with_ledgers(cursor, stored_anchors, stored_anchors)
}
fn owner_with_ledgers(cursor: u64, stored_credits: u64, stored_anchors: u64) -> LiveFrontierOwner {
let binding_epoch = epoch();
let terminal_owner = BindingTerminalOwner {
participant_index: P0,
binding_epoch,
};
let exit_seq = MARKER_SEQ + 1;
let terminal_seq = exit_seq + 1;
let product_seq = terminal_seq + 1;
let sequence = SequenceLedger::try_new(
MARKER_SEQ,
SequenceClaims::new(1, 1, 0, RecoverySequenceReserve::None),
)
.expect("reconcile fixture sequence ledger is valid");
let order = OrderLedger::try_new(
OrderHigh::Allocated(0),
OrderClaims::new(1, 1, false, false).expect("reconcile fixture order claims are valid"),
)
.expect("reconcile fixture order ledger is valid");
let frontiers = ClaimFrontiers::restore(
ClaimFrontiersRestore {
conversation_id: CONVERSATION_ID,
active_identities: vec![FrontierParticipant::new(
P0,
cursor,
FrontierBinding::Bound(binding_epoch),
)],
identity_slot_limit: 1,
retained_floor: u128::from(MARKER_SEQ),
retained_record_limit: 1,
retained_records: vec![RetainedCausalRecord {
delivery_seq: MARKER_SEQ,
admission_order: AdmissionOrder::new(0, CandidatePhase::CompactionMarker, P0),
kind: RetainedCausalRecordKind::CompactionMarker {
participant_index: P0,
provenance: MarkerProvenance::NonProductM,
},
}],
active_marker_anchors: vec![MARKER_SEQ],
historical_marker_deliveries: vec![],
historical_causal_facts: vec![],
sequence: SequenceClaimFrontierRestore {
movable_claims: vec![
MovableSequenceClaim {
delivery_seq: exit_seq,
owner: SequenceDirectOwner::MembershipExit {
participant_index: P0,
},
},
MovableSequenceClaim {
delivery_seq: terminal_seq,
owner: SequenceDirectOwner::BindingTerminal(terminal_owner),
},
],
immutable_candidates: vec![],
products: SequenceProductRangesRestore {
live_times_terminal: vec![TerminalProductRangeRestore {
start: product_seq,
length: 1,
terminal: terminal_owner,
}],
..SequenceProductRangesRestore::default()
},
recovery: None,
},
order: OrderClaimFrontierRestore {
movable_claims: vec![
MovableOrderClaim {
transaction_order: 1,
owner: OrderDirectOwner::ActiveBindingTerminal(terminal_owner),
},
MovableOrderClaim {
transaction_order: 2,
owner: OrderDirectOwner::MembershipExit {
participant_index: P0,
},
},
],
immutable_candidates: vec![],
recovery: None,
},
recovery_marker_delivery_seq: None,
},
sequence,
order,
)
.expect("reconcile fixture restores from the canonical cold shape");
let accounting = ClosureAccounting::try_new(
ClosureState::Clear,
stored_credits,
stored_anchors,
0,
0,
ResourceVector::default(),
WideResourceVector::default(),
ResourceVector::new(16, 1024),
0,
2,
)
.expect("reconcile fixture accounting is valid");
LiveFrontierOwner::from_test_parts(frontiers, accounting, vec![], 1)
}
#[test]
fn reconcile_retires_the_orphaned_anchor_and_is_idempotent() {
let mut owner = owner_with(MARKER_SEQ, 1);
assert_eq!(owner.frontiers().unaccepted_marker_anchor_count(), 0);
assert_eq!(owner.closure_accounting().marker_anchors(), 1);
assert_eq!(
owner.reconcile_marker_anchor_ledgers(),
MarkerAnchorReconcile::RetiredOrphans(1)
);
assert_eq!(owner.closure_accounting().marker_anchors(), 0);
assert_eq!(
owner.reconcile_marker_anchor_ledgers(),
MarkerAnchorReconcile::InStep
);
assert_eq!(owner.closure_accounting().marker_anchors(), 0);
}
#[test]
fn reconcile_leaves_an_in_step_unaccepted_marker_alone() {
let mut owner = owner_with(MARKER_SEQ - 1, 1);
assert_eq!(owner.frontiers().unaccepted_marker_anchor_count(), 1);
assert_eq!(
owner.reconcile_marker_anchor_ledgers(),
MarkerAnchorReconcile::InStep
);
assert_eq!(owner.closure_accounting().marker_anchors(), 1);
}
#[test]
fn reconcile_reminds_the_derived_ahead_direction() {
let mut owner = owner_with_ledgers(MARKER_SEQ - 1, 1, 0);
assert_eq!(owner.frontiers().unaccepted_marker_anchor_count(), 1);
assert_eq!(owner.closure_accounting().marker_capacity_credits(), 1);
assert_eq!(owner.closure_accounting().marker_anchors(), 0);
assert_eq!(
owner.reconcile_marker_anchor_ledgers(),
MarkerAnchorReconcile::RemintedDeficit(1)
);
assert_eq!(owner.closure_accounting().marker_anchors(), 1);
assert_eq!(
owner.reconcile_marker_anchor_ledgers(),
MarkerAnchorReconcile::InStep
);
assert_eq!(owner.closure_accounting().marker_anchors(), 1);
}
#[test]
fn reconcile_refuses_a_remint_the_accounting_arithmetic_cannot_hold() {
let mut owner = owner_with_ledgers(MARKER_SEQ - 1, 0, 0);
assert_eq!(owner.frontiers().unaccepted_marker_anchor_count(), 1);
assert_eq!(owner.closure_accounting().marker_capacity_credits(), 0);
assert_eq!(
owner.reconcile_marker_anchor_ledgers(),
MarkerAnchorReconcile::Refused {
census: 1,
stored: 0
}
);
assert_eq!(owner.closure_accounting().marker_anchors(), 0);
}
const UNIT: u64 = 10;
const WEDGE_MARKER_SEQ: u64 = 1;
const WEDGE_ORDINARY_SEQ: u64 = 2;
type Fingerprint = [u8; 32];
fn wedge_records() -> Vec<RetainedCausalRecord> {
vec![
RetainedCausalRecord {
delivery_seq: WEDGE_MARKER_SEQ,
admission_order: AdmissionOrder::new(0, CandidatePhase::CompactionMarker, P0),
kind: RetainedCausalRecordKind::CompactionMarker {
participant_index: P0,
provenance: MarkerProvenance::NonProductM,
},
},
RetainedCausalRecord {
delivery_seq: WEDGE_ORDINARY_SEQ,
admission_order: AdmissionOrder::new(1, CandidatePhase::OrdinaryRecord, P0),
kind: RetainedCausalRecordKind::OrdinaryRecord {
participant_index: P0,
},
},
]
}
fn wedge_charges() -> Vec<RetainedRecordCharge> {
wedge_records()
.iter()
.map(|record| {
RetainedRecordCharge::new(
record.delivery_seq,
record.admission_order,
ResourceVector::new(1, UNIT),
)
})
.collect()
}
fn wedge_frontiers(binding_epoch: BindingEpoch) -> ClaimFrontiers {
let terminal = BindingTerminalOwner {
participant_index: P0,
binding_epoch,
};
ClaimFrontiers::restore(
ClaimFrontiersRestore {
conversation_id: CONVERSATION_ID,
active_identities: vec![FrontierParticipant::new(
P0,
0,
FrontierBinding::Bound(binding_epoch),
)],
identity_slot_limit: 1,
retained_floor: u128::from(WEDGE_MARKER_SEQ),
retained_record_limit: 8,
retained_records: wedge_records(),
active_marker_anchors: vec![WEDGE_MARKER_SEQ],
historical_marker_deliveries: vec![],
historical_causal_facts: vec![],
sequence: SequenceClaimFrontierRestore {
movable_claims: vec![
MovableSequenceClaim {
delivery_seq: 3,
owner: SequenceDirectOwner::MembershipExit {
participant_index: P0,
},
},
MovableSequenceClaim {
delivery_seq: 4,
owner: SequenceDirectOwner::BindingTerminal(terminal),
},
],
immutable_candidates: vec![],
products: SequenceProductRangesRestore {
live_times_terminal: vec![TerminalProductRangeRestore {
start: 5,
length: 1,
terminal,
}],
..SequenceProductRangesRestore::default()
},
recovery: None,
},
order: OrderClaimFrontierRestore {
movable_claims: vec![
MovableOrderClaim {
transaction_order: 2,
owner: OrderDirectOwner::ActiveBindingTerminal(terminal),
},
MovableOrderClaim {
transaction_order: 3,
owner: OrderDirectOwner::MembershipExit {
participant_index: P0,
},
},
],
immutable_candidates: vec![],
recovery: None,
},
recovery_marker_delivery_seq: None,
},
SequenceLedger::try_new(
WEDGE_ORDINARY_SEQ,
SequenceClaims::new(1, 1, 0, RecoverySequenceReserve::None),
)
.expect("wedge fixture sequence ledger is valid"),
OrderLedger::try_new(
OrderHigh::Allocated(1),
OrderClaims::new(1, 1, false, false).expect("wedge fixture order claims are valid"),
)
.expect("wedge fixture order ledger is valid"),
)
.expect("wedge fixture restores from the canonical cold shape")
}
fn wedge_accounting(anchors: u64) -> ClosureAccounting {
ClosureAccounting::try_new(
ClosureState::Clear,
1,
anchors,
0,
0,
ResourceVector::default(),
WideResourceVector::new(2, u128::from(2 * UNIT)),
ResourceVector::new(12, 12 * UNIT),
0,
2,
)
.expect("wedge fixture accounting is valid")
}
fn wedge_member(generation: Generation) -> LiveMember<Fingerprint> {
LiveMember::restore(LiveMemberRestore {
participant_id: P0,
conversation_id: CONVERSATION_ID,
generation,
attach_secret: AttachSecret::new([0xA5; 32]),
cursor: 0,
enrollment_fingerprint: EnrollmentFingerprint::new([0xE1; 32]),
latest_terminal: None,
})
.expect("wedge fixture member has consistent identity history")
}
fn wedge_request(generation: Generation) -> RecordAdmission {
RecordAdmission {
conversation_id: CONVERSATION_ID,
participant_id: P0,
capability_generation: generation,
record_admission_attempt_token: RecordAdmissionAttemptToken::new([0xA7; 16]),
payload: vec![0xD2; 4],
}
}
fn wedge_limits() -> OrdinaryProjectionLimits {
OrdinaryProjectionLimits::new(
ResourceVector::new(1, UNIT),
ResourceVector::new(2, 2 * UNIT),
ResourceVector::new(2, 2 * UNIT),
)
}
fn wedge_admission(stored_anchors: u64) -> Result<(), (u64, u64)> {
let generation = Generation::new(1).expect("test generation is nonzero");
let binding_epoch = BindingEpoch::new(ConnectionIncarnation::new(6, 0), generation);
let member = wedge_member(generation);
let binding = BindingState::Bound(ActiveBinding {
participant_id: P0,
conversation_id: CONVERSATION_ID,
binding_epoch,
});
let prestate = RecordAdmissionPrestate::new(
wedge_request(generation),
PresentedIdentity::<Fingerprint, Fingerprint, Fingerprint>::Live(&member),
&binding,
binding_epoch,
ConnectionConversationTracking::AlreadyTracked,
CapacityCounter::try_new(4, 1).expect("wedge fixture connection capacity is valid"),
wedge_accounting(stored_anchors),
ResourceVector::new(1, UNIT),
wedge_frontiers(binding_epoch),
wedge_charges(),
2,
wedge_limits(),
);
match apply_record_admission(prestate, ResourceVector::new(1, UNIT)) {
RecordAdmissionDecision::Commit(_) => Ok(()),
RecordAdmissionDecision::Fault(failure) => match failure.fault() {
RecordAdmissionFault::Projection(OrdinaryProjectionError::MarkerAnchorAccounting {
derived,
stored,
}) => Err((*derived, *stored)),
other => panic!("wedge fixture faulted on an unrelated reason: {other:?}"),
},
other => panic!("wedge fixture did not reach a commit-or-fault decision: {other:?}"),
}
}
#[test]
fn the_wedged_admission_commits_once_the_reconcile_reminds_the_lost_anchor() {
assert_eq!(wedge_admission(0), Err((1, 0)));
let binding_epoch = BindingEpoch::new(
ConnectionIncarnation::new(6, 0),
Generation::new(1).expect("test generation is nonzero"),
);
let mut owner = LiveFrontierOwner::from_test_parts(
wedge_frontiers(binding_epoch),
wedge_accounting(0),
wedge_charges(),
8,
);
assert_eq!(
owner.reconcile_marker_anchor_ledgers(),
MarkerAnchorReconcile::RemintedDeficit(1)
);
assert_eq!(owner.closure_accounting().marker_anchors(), 1);
assert_eq!(
wedge_admission(owner.closure_accounting().marker_anchors()),
Ok(())
);
}