1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//! Per-conversation and per-participant stage-8 occupancy, derived from the
//! conversation authority's own replayed state (split from
//! [`super::capacity`] under the 500-code-line lens).
//!
//! Nothing here is durable or shared: every value is a pure function of one
//! conversation's slots and the operation's admitted clock read. The
//! server-scope ledger lives in [`super::capacity`].
use super::capacity::{ConversationContribution, OccupancyEntry, ResourceKind};
use super::state::{ConversationAuthority, Slot, StateError};
impl ConversationAuthority {
/// Request-time expiry of retained provenance fingerprints (contract
/// R-C0: the non-secret fingerprint remains only through its provenance
/// deadline). Classification never depends on physical retention — the
/// generation-window witness in the attach token phase reproduces the
/// same answers — so pruning is purely the memory bound.
pub(super) fn prune_expired_provenance(&mut self, now: u128) {
for slot in self.slots.values_mut() {
slot.attach_provenance
.retain(|_, record| now < record.provenance_expires_at);
}
}
/// In-window provenance-fingerprint occupancy across every slot (the
/// stage-8 `ProvenanceConversation` scope).
///
/// # Errors
///
/// Returns a [`StateError`] invariant if the sum leaves the u64 domain.
pub(super) fn provenance_occupancy(&self, now: u128) -> Result<u64, StateError> {
let mut total: u64 = 0;
for slot in self.slots.values() {
total = total
.checked_add(slot.provenance_occupancy(now)?)
.ok_or_else(|| {
StateError::invariant(
"conversation provenance occupancy exceeds the u64 domain",
)
})?;
}
Ok(total)
}
/// Enrollment-token bytes of one enrolled participant (the permanent
/// token→identity index inverted for ledger entry keys).
///
/// # Errors
///
/// Returns a [`StateError`] invariant for a slot without a token — the
/// enrollment commit always writes both.
pub(super) fn enrollment_token_bytes(
&self,
participant_id: u64,
) -> Result<[u8; 16], StateError> {
self.tokens
.iter()
.find_map(|(token, mapped)| (*mapped == participant_id).then_some(*token))
.ok_or_else(|| {
StateError::invariant("enrolled participant slot has no enrollment token mapping")
})
}
/// Derives this conversation's complete server-scope contribution from
/// its replayed state: every in-window receipt body and provenance
/// fingerprint plus the reserved identity slots.
///
/// # Errors
///
/// Returns a [`StateError`] invariant when the token index and the slot
/// map disagree (a drifted replay).
pub(super) fn capacity_contribution(
&self,
now: u128,
) -> Result<ConversationContribution, StateError> {
let mut entries = Vec::new();
for (token, participant_id) in &self.tokens {
if let Some(slot) = self.slots.get(participant_id) {
if slot.enrollment_receipt_ended.is_none()
&& now < slot.enrollment_receipt_expires_at
{
entries.push(OccupancyEntry {
expires_at: slot.enrollment_receipt_expires_at,
conversation_id: self.conversation_id,
participant_id: *participant_id,
kind: ResourceKind::EnrollmentReceipt,
token: *token,
});
}
if now < slot.enrollment_provenance_expires_at {
entries.push(OccupancyEntry {
expires_at: slot.enrollment_provenance_expires_at,
conversation_id: self.conversation_id,
participant_id: *participant_id,
kind: ResourceKind::EnrollmentProvenance,
token: *token,
});
}
} else if !self.retired.contains_key(participant_id) {
return Err(StateError::invariant(
"enrollment token maps to neither a live nor retired participant",
));
}
}
for (participant_id, slot) in &self.slots {
if let Some(attach) = slot.attach.as_ref() {
if now < attach.receipt_expires_at {
entries.push(OccupancyEntry {
expires_at: attach.receipt_expires_at,
conversation_id: self.conversation_id,
participant_id: *participant_id,
kind: ResourceKind::AttachReceipt,
token: attach.token.into_bytes(),
});
}
if now < attach.provenance_expires_at {
entries.push(OccupancyEntry {
expires_at: attach.provenance_expires_at,
conversation_id: self.conversation_id,
participant_id: *participant_id,
kind: ResourceKind::AttachProvenance,
token: attach.token.into_bytes(),
});
}
}
for (token, record) in &slot.attach_provenance {
if now < record.provenance_expires_at {
entries.push(OccupancyEntry {
expires_at: record.provenance_expires_at,
conversation_id: self.conversation_id,
participant_id: *participant_id,
kind: ResourceKind::AttachProvenance,
token: *token,
});
}
}
}
Ok(ConversationContribution {
identity: self.next_participant,
entries,
})
}
}
impl Slot {
/// Live secret-bearing receipt occupancy for this participant (the
/// stage-8 `LiveReceiptParticipant` scope): the enrollment receipt while
/// unended and inside its own window, plus the current attach receipt
/// inside its window (superseded receipts were already retired into
/// provenance records).
pub(super) fn live_receipt_occupancy(&self, now: u128) -> u64 {
u64::from(
self.enrollment_receipt_ended.is_none() && now < self.enrollment_receipt_expires_at,
) + u64::from(
self.attach
.as_ref()
.is_some_and(|attach| now < attach.receipt_expires_at),
)
}
/// In-window provenance-fingerprint occupancy for this participant (the
/// stage-8 `ProvenanceParticipant` scope). Fingerprints exist from their
/// operation's commit through their own provenance deadline: the
/// enrollment fingerprint, the current attach receipt's fingerprint, and
/// every retained record of a retired rotation.
///
/// # Errors
///
/// Returns a [`StateError`] invariant if the count leaves the u64 domain.
pub(super) fn provenance_occupancy(&self, now: u128) -> Result<u64, StateError> {
let retained = self
.attach_provenance
.values()
.filter(|record| now < record.provenance_expires_at)
.count();
let retained = u64::try_from(retained).map_err(|_| {
StateError::invariant("participant provenance occupancy exceeds the u64 domain")
})?;
retained
.checked_add(u64::from(now < self.enrollment_provenance_expires_at))
.and_then(|total| {
total.checked_add(u64::from(
self.attach
.as_ref()
.is_some_and(|attach| now < attach.provenance_expires_at),
))
})
.ok_or_else(|| {
StateError::invariant("participant provenance occupancy exceeds the u64 domain")
})
}
}