Skip to main content

liminal_protocol/algebra/
capacity.rs

1use super::types::{
2    BaselineError, MandatoryCapacity, RecoveryTransfer, RecoveryTransferError, ResourceDimension,
3    ResourceVector, WideResourceVector, widen_u64,
4};
5
6/// Computes `B = S + ((I - C) × marker_max)` componentwise.
7///
8/// Every operand is widened to `u128` before subtraction, multiplication, or
9/// addition. This keeps the printed suboperation order without importing the
10/// occurrence-array machinery excluded by `docs/design/LP-EXTRACTION-GOAL.md`.
11///
12/// # Errors
13///
14/// Returns [`BaselineError`] when `C > I`.
15pub const fn retained_baseline(
16    retained_charge: ResourceVector,
17    identity_slots: u64,
18    marker_credits: u64,
19    marker_max: ResourceVector,
20) -> Result<WideResourceVector, BaselineError> {
21    if marker_credits > identity_slots {
22        return Err(BaselineError::MarkerCreditsExceedIdentitySlots {
23            identity_slots,
24            marker_credits,
25        });
26    }
27
28    let uncredited_slots = widen_u64(identity_slots) - widen_u64(marker_credits);
29    let marker_entries = uncredited_slots * widen_u64(marker_max.entries);
30    let marker_bytes = uncredited_slots * widen_u64(marker_max.bytes);
31    Ok(WideResourceVector::new(
32        widen_u64(retained_charge.entries) + marker_entries,
33        widen_u64(retained_charge.bytes) + marker_bytes,
34    ))
35}
36
37/// Returns the first failed component of `B + Q + K <= cap`.
38///
39/// Entries are checked before bytes, matching the contract's refusal
40/// precedence. An arithmetic overflow is a failure of that component.
41#[must_use]
42pub const fn zero_debt_capacity_failure(
43    baseline: WideResourceVector,
44    mandatory_bound: ResourceVector,
45    recovery_claim: ResourceVector,
46    configured_cap: ResourceVector,
47) -> Option<ResourceDimension> {
48    if !component_fits(
49        baseline.entries,
50        mandatory_bound.entries,
51        recovery_claim.entries,
52        configured_cap.entries,
53    ) {
54        return Some(ResourceDimension::Entries);
55    }
56    if !component_fits(
57        baseline.bytes,
58        mandatory_bound.bytes,
59        recovery_claim.bytes,
60        configured_cap.bytes,
61    ) {
62        return Some(ResourceDimension::Bytes);
63    }
64    None
65}
66
67/// Checks the zero-debt ordinary-admission invariant `B + Q + K <= cap`.
68#[must_use]
69pub const fn zero_debt_admission(
70    baseline: WideResourceVector,
71    mandatory_bound: ResourceVector,
72    recovery_claim: ResourceVector,
73    configured_cap: ResourceVector,
74) -> bool {
75    zero_debt_capacity_failure(baseline, mandatory_bound, recovery_claim, configured_cap).is_none()
76}
77
78/// Computes the mandatory-class debt and its two required checks.
79///
80/// The result contains
81/// `d' = max(0, B' + Q + K_remaining' - cap)`, the absolute-fit check
82/// `B' + K_remaining' <= cap`, and the debt bound `d' <= Q`.
83#[must_use]
84pub const fn mandatory_capacity(
85    resulting_baseline: WideResourceVector,
86    mandatory_bound: ResourceVector,
87    remaining_recovery_claim: ResourceVector,
88    configured_cap: ResourceVector,
89) -> MandatoryCapacity {
90    let entries = mandatory_component(
91        resulting_baseline.entries,
92        mandatory_bound.entries,
93        remaining_recovery_claim.entries,
94        configured_cap.entries,
95    );
96    let bytes = mandatory_component(
97        resulting_baseline.bytes,
98        mandatory_bound.bytes,
99        remaining_recovery_claim.bytes,
100        configured_cap.bytes,
101    );
102
103    MandatoryCapacity {
104        debt: WideResourceVector::new(entries.debt, bytes.debt),
105        absolute_fit: entries.absolute_fit && bytes.absolute_fit,
106        debt_within_mandatory_bound: entries.debt_within_bound && bytes.debt_within_bound,
107    }
108}
109
110/// Transfers an exact recovery record charge from `K_remaining` into `B`.
111///
112/// The candidate is counted once: `B' = B_removed + r` and
113/// `K_remaining' = K_remaining - r`.
114///
115/// # Errors
116///
117/// Returns [`RecoveryTransferError`] for the first component in which `r`
118/// exceeds `K_remaining` or the widened baseline sum is unrepresentable.
119pub const fn recovery_transfer(
120    baseline_after_removals: WideResourceVector,
121    remaining_recovery_claim: ResourceVector,
122    charge: ResourceVector,
123) -> Result<RecoveryTransfer, RecoveryTransferError> {
124    if charge.entries > remaining_recovery_claim.entries {
125        return Err(RecoveryTransferError {
126            dimension: ResourceDimension::Entries,
127        });
128    }
129    if charge.bytes > remaining_recovery_claim.bytes {
130        return Err(RecoveryTransferError {
131            dimension: ResourceDimension::Bytes,
132        });
133    }
134
135    let Some(entries) = baseline_after_removals
136        .entries
137        .checked_add(widen_u64(charge.entries))
138    else {
139        return Err(RecoveryTransferError {
140            dimension: ResourceDimension::Entries,
141        });
142    };
143    let Some(bytes) = baseline_after_removals
144        .bytes
145        .checked_add(widen_u64(charge.bytes))
146    else {
147        return Err(RecoveryTransferError {
148            dimension: ResourceDimension::Bytes,
149        });
150    };
151
152    Ok(RecoveryTransfer {
153        baseline: WideResourceVector::new(entries, bytes),
154        remaining_recovery_claim: ResourceVector::new(
155            remaining_recovery_claim.entries - charge.entries,
156            remaining_recovery_claim.bytes - charge.bytes,
157        ),
158    })
159}
160
161/// Checks the only legal no-edge state: zero debt plus full-K fit.
162#[must_use]
163pub const fn no_edge_legal(
164    debt: WideResourceVector,
165    baseline: WideResourceVector,
166    mandatory_bound: ResourceVector,
167    full_recovery_claim: ResourceVector,
168    configured_cap: ResourceVector,
169) -> bool {
170    debt.is_zero()
171        && zero_debt_admission(
172            baseline,
173            mandatory_bound,
174            full_recovery_claim,
175            configured_cap,
176        )
177}
178
179const fn component_fits(baseline: u128, q: u64, k: u64, cap: u64) -> bool {
180    let Some(with_q) = baseline.checked_add(widen_u64(q)) else {
181        return false;
182    };
183    let Some(required) = with_q.checked_add(widen_u64(k)) else {
184        return false;
185    };
186    required <= widen_u64(cap)
187}
188
189struct MandatoryComponent {
190    debt: u128,
191    absolute_fit: bool,
192    debt_within_bound: bool,
193}
194
195const fn mandatory_component(baseline: u128, q: u64, k: u64, cap: u64) -> MandatoryComponent {
196    let q = widen_u64(q);
197    let k = widen_u64(k);
198    let cap = widen_u64(cap);
199    let debt = match baseline.checked_add(q) {
200        Some(with_q) => match with_q.checked_add(k) {
201            Some(required) => required.saturating_sub(cap),
202            None => u128::MAX,
203        },
204        None => u128::MAX,
205    };
206    let absolute_fit = match baseline.checked_add(k) {
207        Some(required) => required <= cap,
208        None => false,
209    };
210    MandatoryComponent {
211        debt,
212        absolute_fit,
213        debt_within_bound: debt <= q,
214    }
215}