use alloc::vec::Vec;
use crate::wire::{ParticipantDelivery, ParticipantRecord};
use super::super::{
ClaimFrontiers, ClosureAccounting, ClosureState, Event, MarkerDelivery, ObserverProjection,
StoredEdge,
claim_frontier::{MarkerDrainCoreError, ValidatedMarkerRecord},
};
use super::RetainedRecordCharge;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MarkerDrainError {
NoCandidate,
BindingTerminalFirst,
SequenceNotNext,
CurrentEdgeMismatch,
CausalMajorNotAllocated,
MissingOrderCandidate,
ResultingLedger,
AuthorityMismatch,
MarkerChargeKey,
MarkerEntryCharge,
ResultingAccounting,
}
#[derive(Debug, PartialEq, Eq)]
pub struct MarkerDeliveryProjection {
delivery: ParticipantDelivery,
}
impl MarkerDeliveryProjection {
#[must_use]
pub const fn delivery(&self) -> &ParticipantDelivery {
&self.delivery
}
#[must_use]
pub fn into_delivery(self) -> ParticipantDelivery {
self.delivery
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct MarkerDrainCommit {
frontiers: ClaimFrontiers,
closure_accounting: ClosureAccounting,
retained_charges: Vec<RetainedRecordCharge>,
marker_successor: StoredEdge,
delivery_projection: MarkerDeliveryProjection,
record: ValidatedMarkerRecord,
}
impl MarkerDrainCommit {
#[must_use]
pub const fn frontiers(&self) -> &ClaimFrontiers {
&self.frontiers
}
#[must_use]
pub const fn closure(&self) -> ClosureState {
self.closure_accounting.state()
}
#[must_use]
pub const fn closure_accounting(&self) -> ClosureAccounting {
self.closure_accounting
}
#[must_use]
pub fn retained_charges(&self) -> &[RetainedRecordCharge] {
&self.retained_charges
}
#[must_use]
pub const fn marker_successor(&self) -> StoredEdge {
self.marker_successor
}
#[must_use]
pub const fn delivery_projection(&self) -> &MarkerDeliveryProjection {
&self.delivery_projection
}
#[must_use]
pub fn into_parts(
self,
) -> (
ClaimFrontiers,
ClosureAccounting,
Vec<RetainedRecordCharge>,
StoredEdge,
MarkerDeliveryProjection,
) {
self.record.consume();
(
self.frontiers,
self.closure_accounting,
self.retained_charges,
self.marker_successor,
self.delivery_projection,
)
}
#[cfg(test)]
pub(super) fn into_record_for_test(self) -> ValidatedMarkerRecord {
self.record
}
}
pub fn drain_next_marker(
frontiers: ClaimFrontiers,
current_accounting: ClosureAccounting,
mut retained_charges: Vec<RetainedRecordCharge>,
marker_charge: RetainedRecordCharge,
) -> Result<MarkerDrainCommit, MarkerDrainError> {
let core = frontiers.drain_next_marker_core().map_err(map_core_error)?;
let (frontiers, candidate, record) = core.into_parts();
let candidate_conversation_id = candidate.conversation_id();
let candidate_participant = candidate.participant_id();
let candidate_sequence = candidate.delivery_seq();
let candidate_target = candidate.target_binding();
let candidate_provenance = candidate.provenance();
let delivery_projection = MarkerDeliveryProjection {
delivery: ParticipantDelivery {
conversation_id: candidate_conversation_id,
delivery_seq: candidate_sequence,
record: ParticipantRecord::HistoryCompacted {
affected_participant_id: candidate_participant,
abandoned_after: candidate.abandoned_after(),
abandoned_through: candidate.abandoned_through(),
physical_floor_at_decision: candidate.physical_floor_at_decision(),
},
},
};
let marker_successor = MarkerDelivery::successor_from_validated_candidate(candidate);
if record.conversation_id() != candidate_conversation_id
|| record.participant_id() != candidate_participant
|| record.delivery_seq() != candidate_sequence
|| record.target_binding() != candidate_target
|| record.provenance() != candidate_provenance
{
return Err(MarkerDrainError::AuthorityMismatch);
}
if marker_charge.delivery_seq() != record.delivery_seq()
|| marker_charge.admission_order() != record.admission_order()
{
return Err(MarkerDrainError::MarkerChargeKey);
}
if marker_charge.encoded_charge().entries != 1 {
return Err(MarkerDrainError::MarkerEntryCharge);
}
let closure = apply_marker_append(current_accounting.state(), candidate_sequence)?;
let closure_accounting = ClosureAccounting::try_new(
closure,
current_accounting.marker_capacity_credits(),
current_accounting.marker_anchors(),
current_accounting.edge_sequence_claims(),
current_accounting.edge_order_position_claims(),
current_accounting.edge_k_remaining(),
current_accounting.baseline(),
current_accounting.configured_cap(),
current_accounting.episode_churn_used(),
current_accounting.episode_churn_limit(),
)
.map_err(|_| MarkerDrainError::ResultingAccounting)?;
retained_charges.push(marker_charge);
if retained_charges.len() != frontiers.retained_records().len()
|| !retained_charges
.iter()
.zip(frontiers.retained_records())
.all(|(charge, retained)| {
charge.delivery_seq() == retained.delivery_seq
&& charge.admission_order() == retained.admission_order
})
{
return Err(MarkerDrainError::MarkerChargeKey);
}
Ok(MarkerDrainCommit {
frontiers,
closure_accounting,
retained_charges,
marker_successor,
delivery_projection,
record,
})
}
fn apply_marker_append(
current: ClosureState,
marker_delivery_seq: u64,
) -> Result<ClosureState, MarkerDrainError> {
let event = Event::marker_appended(marker_delivery_seq, marker_delivery_seq);
match current {
ClosureState::Clear => Ok(ClosureState::Clear),
ClosureState::Owed {
debt,
edge: StoredEdge::ObserverProjection(projection),
} => {
let successor = projection
.later_projection_after_marker(
&event,
debt,
ObserverProjection::new(marker_delivery_seq),
)
.ok_or(MarkerDrainError::CurrentEdgeMismatch)?;
projection
.marker_appended(debt, event, successor)
.map_err(|_| MarkerDrainError::CurrentEdgeMismatch)
}
ClosureState::Owed {
debt,
edge: StoredEdge::PhysicalCompaction(compaction),
} => compaction
.marker_appended(debt, event)
.map_err(|_| MarkerDrainError::CurrentEdgeMismatch),
ClosureState::Owed { .. } => Err(MarkerDrainError::CurrentEdgeMismatch),
}
}
const fn map_core_error(error: MarkerDrainCoreError) -> MarkerDrainError {
match error {
MarkerDrainCoreError::NoCandidate => MarkerDrainError::NoCandidate,
MarkerDrainCoreError::BindingTerminalFirst => MarkerDrainError::BindingTerminalFirst,
MarkerDrainCoreError::SequenceNotNext => MarkerDrainError::SequenceNotNext,
MarkerDrainCoreError::CausalMajorNotAllocated => MarkerDrainError::CausalMajorNotAllocated,
MarkerDrainCoreError::MissingOrderCandidate => MarkerDrainError::MissingOrderCandidate,
MarkerDrainCoreError::ResultingLedger => MarkerDrainError::ResultingLedger,
}
}