-
Notifications
You must be signed in to change notification settings - Fork 3
merge: sync upstream aeyakovenko/percolator v12.1 (136 commits) #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
70d5b6e
700b856
dc2d67f
4a7edee
d1312ed
da085a0
85d0b28
9bc0fab
36381d6
1f0892c
aa85ab1
f65ec5d
8823e96
294b591
1ab3574
ce88753
c669d8a
9c55eef
c393329
7d9603e
b5d0b95
ff559c9
af8cb23
857a102
ce12aac
6bb68ee
b65a14a
c480367
2c1029f
c8a5f0a
4777cf5
558e87a
1afa183
95d4937
f7d2de8
d114bf2
212e198
d09d36d
d64bab9
ddd59c9
1a3b60b
9d962d7
c7f8f68
173e6ca
b124b27
10312f5
f4930c2
60d3629
fed1be7
04762c8
82425cd
afacca8
b246b12
0afb13a
b283f16
1d078bb
ff71199
640055a
b261865
3b4f384
ce8512b
f832aca
9feedae
aadea01
7565a1a
13a22d3
a7e2fff
17d7104
b958b47
499b58b
19db9bb
d33b291
030b2b2
48f4452
d1e3ba1
ff0b25c
ad49ad1
7830f1d
b576d65
a97a416
3db8d07
94c3679
f6dd879
9705213
3e2ea1d
a08a6fe
893c8da
b82d6ba
e4be4db
b64ea60
091163e
6b6a0a2
a4c4077
cee584a
03e37a9
4b84390
58066af
cd0b3df
a55fe45
a5d2ff5
39ae652
1f60022
0403700
be77a9d
6c963dd
8181322
c636dac
a9b5017
fa3ec3c
e357d43
a874acd
79c0c92
55013a8
2430112
a5197e4
0082064
acd4361
d6cbe0e
5d53701
2598a93
d94d064
03737b1
c7e0cb0
fb90d8d
f12a651
6f85a89
4e7566e
5bb71a3
2338184
0521252
60bc843
7575941
1ad0ec7
d9a09ff
733ed6d
a3e7e88
d968fe5
5f1f549
c3c720b
dd8e946
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -252,7 +252,12 @@ impl U256 { | |
| /// Create from low 128 bits and high 128 bits. | ||
| #[inline] | ||
| pub const fn new(lo: u128, hi: u128) -> Self { | ||
| Self([lo as u64, (lo >> 64) as u64, hi as u64, (hi >> 64) as u64]) | ||
| Self([ | ||
| lo as u64, | ||
| (lo >> 64) as u64, | ||
| hi as u64, | ||
| (hi >> 64) as u64, | ||
| ]) | ||
| } | ||
|
|
||
| #[inline] | ||
|
|
@@ -867,7 +872,12 @@ impl I256 { | |
| } | ||
|
|
||
| fn from_lo_hi(lo: u128, hi: u128) -> Self { | ||
| Self([lo as u64, (lo >> 64) as u64, hi as u64, (hi >> 64) as u64]) | ||
| Self([ | ||
| lo as u64, | ||
| (lo >> 64) as u64, | ||
| hi as u64, | ||
| (hi >> 64) as u64, | ||
| ]) | ||
| } | ||
|
|
||
| fn as_raw_u256(self) -> U256 { | ||
|
|
@@ -943,17 +953,18 @@ fn widening_mul_u128(a: u128, b: u128) -> (u128, u128) { | |
| let b_lo = b as u64 as u128; | ||
| let b_hi = (b >> 64) as u64 as u128; | ||
|
|
||
| let ll = a_lo * b_lo; // 0..2^128 | ||
| let lh = a_lo * b_hi; // 0..2^128 | ||
| let hl = a_hi * b_lo; // 0..2^128 | ||
| let hh = a_hi * b_hi; // 0..2^128 | ||
| let ll = a_lo * b_lo; // 0..2^128 | ||
| let lh = a_lo * b_hi; // 0..2^128 | ||
| let hl = a_hi * b_lo; // 0..2^128 | ||
| let hh = a_hi * b_hi; // 0..2^128 | ||
|
|
||
| // Accumulate: | ||
| // result = ll + (lh + hl) << 64 + hh << 128 | ||
| let (mid, mid_carry) = lh.overflowing_add(hl); // mid_carry means +2^128 | ||
|
|
||
| let (lo, lo_carry) = ll.overflowing_add(mid << 64); | ||
| let hi = hh + (mid >> 64) + ((mid_carry as u128) << 64) + (lo_carry as u128); | ||
| let hi = hh + (mid >> 64) + ((mid_carry as u128) << 64) | ||
| + (lo_carry as u128); | ||
| // lo_carry is at most 1, captured in hi | ||
|
|
||
| (lo, hi) | ||
|
|
@@ -1100,7 +1111,6 @@ impl U512 { | |
| } | ||
|
|
||
| /// Shift left by `bits`. Saturates to zero if bits >= 512. | ||
| #[allow(clippy::needless_range_loop)] | ||
| fn shl_u512(self, bits: u32) -> Self { | ||
| if bits >= 512 { | ||
| return Self::ZERO; | ||
|
|
@@ -1122,7 +1132,6 @@ impl U512 { | |
| } | ||
|
|
||
| /// Shift right by `bits`. | ||
| #[allow(clippy::needless_range_loop)] | ||
| fn shr_u512(self, bits: u32) -> Self { | ||
| if bits >= 512 { | ||
| return Self::ZERO; | ||
|
|
@@ -1144,7 +1153,6 @@ impl U512 { | |
| } | ||
|
|
||
| /// Subtract rhs from self. Assumes self >= rhs. | ||
| #[allow(clippy::needless_range_loop)] | ||
| fn sub_u512(self, rhs: Self) -> Self { | ||
| let mut result = [0u128; 4]; | ||
| let mut borrow = false; | ||
|
|
@@ -1250,10 +1258,7 @@ impl U512 { | |
| /// Computes floor(n / d) where d > 0. Uses truncation toward zero, then | ||
| /// adjusts: if n < 0 and there is a non-zero remainder, subtract 1. | ||
| pub fn floor_div_signed_conservative(n: I256, d: U256) -> I256 { | ||
| assert!( | ||
| !d.is_zero(), | ||
| "floor_div_signed_conservative: zero denominator" | ||
| ); | ||
| assert!(!d.is_zero(), "floor_div_signed_conservative: zero denominator"); | ||
|
|
||
| if n.is_zero() { | ||
| return I256::ZERO; | ||
|
|
@@ -1288,8 +1293,7 @@ pub fn floor_div_signed_conservative(n: I256, d: U256) -> I256 { | |
| let q_final = if r.is_zero() { | ||
| q | ||
| } else { | ||
| q.checked_add(U256::ONE) | ||
| .expect("floor_div quotient overflow") | ||
| q.checked_add(U256::ONE).expect("floor_div quotient overflow") | ||
| }; | ||
|
|
||
| // Negate q_final to get the negative I256 result. | ||
|
|
@@ -1303,26 +1307,27 @@ pub fn floor_div_signed_conservative(n: I256, d: U256) -> I256 { | |
| } | ||
| } | ||
|
|
||
| /// i128 version of floor_div_signed_conservative for compact code paths. | ||
| /// Native i128 floor division: floor(n / d) for positive d, rounding toward | ||
| /// negative infinity. Mirrors `floor_div_signed_conservative` but uses native | ||
| /// i128/u128 arithmetic for the funding-term computation (spec §5.4). | ||
| pub fn floor_div_signed_conservative_i128(n: i128, d: u128) -> i128 { | ||
| assert!( | ||
| d != 0, | ||
| "floor_div_signed_conservative_i128: zero denominator" | ||
| ); | ||
| assert!(d != 0, "floor_div_signed_conservative_i128: zero denominator"); | ||
|
|
||
| if n == 0 { | ||
| return 0; | ||
| } | ||
|
|
||
| if n > 0 { | ||
| // Non-negative: floor = truncation | ||
| (n as u128 / d) as i128 | ||
| } else { | ||
| // Negative: floor(n/d) = -(|n| / d) - (if |n| % d != 0 then 1 else 0) | ||
| let abs_n = n.unsigned_abs(); | ||
| let q = abs_n / d; | ||
| let r = abs_n % d; | ||
| let q_final = if r != 0 { q + 1 } else { q }; | ||
| assert!( | ||
| q_final <= i128::MAX as u128, | ||
| "floor_div_signed_conservative_i128: result out of range" | ||
| ); | ||
| assert!(q_final <= i128::MAX as u128, | ||
| "floor_div_signed_conservative_i128: result out of range"); | ||
| -(q_final as i128) | ||
|
Comment on lines
1313
to
1331
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle the exact
Suggested fix pub fn floor_div_signed_conservative_i128(n: i128, d: u128) -> i128 {
@@
- let q_final = if r != 0 { q + 1 } else { q };
- assert!(q_final <= i128::MAX as u128,
- "floor_div_signed_conservative_i128: result out of range");
- -(q_final as i128)
+ let q_final = if r != 0 { q + 1 } else { q };
+ if q_final == (1u128 << 127) {
+ i128::MIN
+ } else {
+ assert!(q_final <= i128::MAX as u128,
+ "floor_div_signed_conservative_i128: result out of range");
+ -(q_final as i128)
+ }
}
}
@@
if d.is_negative() {
@@
- let mag_u128 = mag.try_into_u128().expect("mag exceeds u128");
- assert!(mag_u128 <= i128::MAX as u128, "wide_signed_mul_div_floor_from_k_pair: mag > i128::MAX");
- -(mag_u128 as i128)
+ let mag_u128 = mag.try_into_u128().expect("mag exceeds u128");
+ if mag_u128 == (1u128 << 127) {
+ i128::MIN
+ } else {
+ assert!(mag_u128 <= i128::MAX as u128, "wide_signed_mul_div_floor_from_k_pair: mag > i128::MAX");
+ -(mag_u128 as i128)
+ }
} else {Also applies to: 1498-1526 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
|
|
@@ -1352,10 +1357,7 @@ pub fn mul_div_floor_u256(a: U256, b: U256, d: U256) -> U256 { | |
| /// Like mul_div_floor_u256 but also returns the remainder. | ||
| /// Returns (floor(a * b / d), (a * b) mod d). | ||
| pub fn mul_div_floor_u256_with_rem(a: U256, b: U256, d: U256) -> (U256, U256) { | ||
| assert!( | ||
| !d.is_zero(), | ||
| "mul_div_floor_u256_with_rem: zero denominator" | ||
| ); | ||
| assert!(!d.is_zero(), "mul_div_floor_u256_with_rem: zero denominator"); | ||
| let product = U512::mul_u256(a, b); | ||
| product.div_rem_by_u256(d) | ||
| } | ||
|
|
@@ -1416,21 +1418,15 @@ pub fn fee_debt_u128_checked(fee_credits: i128) -> u128 { | |
| /// Uses the sign of `k_diff`. Computes `abs_basis * abs(k_diff)` as U512, | ||
| /// then applies floor_div_signed_conservative logic. | ||
| pub fn wide_signed_mul_div_floor(abs_basis: U256, k_diff: I256, denominator: U256) -> I256 { | ||
| assert!( | ||
| !denominator.is_zero(), | ||
| "wide_signed_mul_div_floor: zero denominator" | ||
| ); | ||
| assert!(!denominator.is_zero(), "wide_signed_mul_div_floor: zero denominator"); | ||
|
|
||
| if k_diff.is_zero() || abs_basis.is_zero() { | ||
| return I256::ZERO; | ||
| } | ||
|
|
||
| let negative = k_diff.is_negative(); | ||
| let abs_k = if negative { | ||
| assert!( | ||
| k_diff != I256::MIN, | ||
| "wide_signed_mul_div_floor: k_diff == I256::MIN" | ||
| ); | ||
| assert!(k_diff != I256::MIN, "wide_signed_mul_div_floor: k_diff == I256::MIN"); | ||
| k_diff.abs_u256() | ||
| } else { | ||
| k_diff.abs_u256() | ||
|
|
@@ -1448,15 +1444,13 @@ pub fn wide_signed_mul_div_floor(abs_basis: U256, k_diff: I256, denominator: U25 | |
| let q_final = if r.is_zero() { | ||
| q | ||
| } else { | ||
| q.checked_add(U256::ONE) | ||
| .expect("wide_signed_mul_div_floor quotient overflow") | ||
| q.checked_add(U256::ONE).expect("wide_signed_mul_div_floor quotient overflow") | ||
| }; | ||
| if q_final.is_zero() { | ||
| I256::ZERO | ||
| } else { | ||
| let qi = I256::from_raw_u256(q_final); | ||
| qi.checked_neg() | ||
| .expect("wide_signed_mul_div_floor result out of I256 range") | ||
| qi.checked_neg().expect("wide_signed_mul_div_floor result out of I256 range") | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1473,7 +1467,7 @@ impl I256 { | |
| } | ||
|
|
||
| // ============================================================================ | ||
| // §4.8 v11.5 Native 128-bit Arithmetic Helpers | ||
| // §4.8 v11.31 Native 128-bit Arithmetic Helpers | ||
| // ============================================================================ | ||
|
|
||
| /// Native multiply-divide floor. Product a*b must not overflow u128. Panics on d==0. | ||
|
|
@@ -1488,48 +1482,33 @@ pub fn mul_div_ceil_u128(a: u128, b: u128, d: u128) -> u128 { | |
| assert!(d > 0, "mul_div_ceil_u128: division by zero"); | ||
| let p = a.checked_mul(b).expect("mul_div_ceil_u128: a*b overflow"); | ||
| let q = p / d; | ||
| if !p.is_multiple_of(d) { | ||
| q + 1 | ||
| } else { | ||
| q | ||
| } | ||
| if p % d != 0 { q + 1 } else { q } | ||
| } | ||
|
|
||
| /// Exact wide multiply-divide floor using U256 intermediate. | ||
| /// Used for haircut paths where a*b can exceed u128::MAX. | ||
| pub fn wide_mul_div_floor_u128(a: u128, b: u128, d: u128) -> u128 { | ||
| assert!(d > 0, "wide_mul_div_floor_u128: division by zero"); | ||
| let result = mul_div_floor_u256(U256::from_u128(a), U256::from_u128(b), U256::from_u128(d)); | ||
| result | ||
| .try_into_u128() | ||
| .expect("wide_mul_div_floor_u128: result exceeds u128") | ||
| result.try_into_u128().expect("wide_mul_div_floor_u128: result exceeds u128") | ||
| } | ||
|
|
||
| /// Safe K-difference settlement (spec §4.8 lines 720-732). | ||
| /// Computes K-difference in wide intermediate, then multiplies and divides. | ||
| pub fn wide_signed_mul_div_floor_from_k_pair( | ||
| abs_basis: u128, | ||
| k_now: i128, | ||
| k_then: i128, | ||
| den: u128, | ||
| ) -> i128 { | ||
| pub fn wide_signed_mul_div_floor_from_k_pair(abs_basis: u128, k_then: i128, k_now: i128, den: u128) -> i128 { | ||
| assert!(den > 0, "wide_signed_mul_div_floor_from_k_pair: den == 0"); | ||
| // Compute d = k_now - k_then in wide signed to avoid i128 overflow | ||
| // Compute d = k_now - k_then in wide signed to avoid i128 overflow (spec §4.8) | ||
| let k_now_wide = I256::from_i128(k_now); | ||
| let k_then_wide = I256::from_i128(k_then); | ||
| let d = k_now_wide | ||
| .checked_sub(k_then_wide) | ||
| .expect("K-diff overflow in wide"); | ||
| let d = k_now_wide.checked_sub(k_then_wide).expect("K-diff overflow in wide"); | ||
| if d.is_zero() || abs_basis == 0 { | ||
| return 0i128; | ||
| } | ||
| let abs_d = d.abs_u256(); | ||
| let abs_basis_u256 = U256::from_u128(abs_basis); | ||
| let den_u256 = U256::from_u128(den); | ||
| // p = abs_basis * abs(d), exact wide product | ||
| let p = abs_basis_u256 | ||
| .checked_mul(abs_d) | ||
| .expect("wide product overflow"); | ||
| let p = abs_basis_u256.checked_mul(abs_d).expect("wide product overflow"); | ||
| let (q, rem) = div_rem_u256(p, den_u256); | ||
| if d.is_negative() { | ||
| // mag = q + 1 if r != 0 else q | ||
|
|
@@ -1539,17 +1518,11 @@ pub fn wide_signed_mul_div_floor_from_k_pair( | |
| q | ||
| }; | ||
| let mag_u128 = mag.try_into_u128().expect("mag exceeds u128"); | ||
| assert!( | ||
| mag_u128 <= i128::MAX as u128, | ||
| "wide_signed_mul_div_floor_from_k_pair: mag > i128::MAX" | ||
| ); | ||
| assert!(mag_u128 <= i128::MAX as u128, "wide_signed_mul_div_floor_from_k_pair: mag > i128::MAX"); | ||
| -(mag_u128 as i128) | ||
| } else { | ||
| let q_u128 = q.try_into_u128().expect("quotient exceeds u128"); | ||
| assert!( | ||
| q_u128 <= i128::MAX as u128, | ||
| "wide_signed_mul_div_floor_from_k_pair: q > i128::MAX" | ||
| ); | ||
| assert!(q_u128 <= i128::MAX as u128, "wide_signed_mul_div_floor_from_k_pair: q > i128::MAX"); | ||
| q_u128 as i128 | ||
| } | ||
| } | ||
|
|
@@ -1560,15 +1533,8 @@ pub struct OverI128Magnitude; | |
|
|
||
| /// ADL delta_K representability check. | ||
| /// Returns Ok(v) if the ceil result fits in i128 magnitude, Err otherwise. | ||
| pub fn wide_mul_div_ceil_u128_or_over_i128max( | ||
| a: u128, | ||
| b: u128, | ||
| d: u128, | ||
| ) -> core::result::Result<u128, OverI128Magnitude> { | ||
| assert!( | ||
| d > 0, | ||
| "wide_mul_div_ceil_u128_or_over_i128max: division by zero" | ||
| ); | ||
| pub fn wide_mul_div_ceil_u128_or_over_i128max(a: u128, b: u128, d: u128) -> core::result::Result<u128, OverI128Magnitude> { | ||
| assert!(d > 0, "wide_mul_div_ceil_u128_or_over_i128max: division by zero"); | ||
| let result = mul_div_ceil_u256(U256::from_u128(a), U256::from_u128(b), U256::from_u128(d)); | ||
| match result.try_into_u128() { | ||
| Some(v) if v <= i128::MAX as u128 => Ok(v), | ||
|
|
@@ -1582,7 +1548,7 @@ pub fn saturating_mul_u128_u64(a: u128, b: u64) -> u128 { | |
| return 0; | ||
| } | ||
| let b128 = b as u128; | ||
| a.saturating_mul(b128) | ||
| a.checked_mul(b128).unwrap_or(u128::MAX) | ||
| } | ||
|
|
||
| // ============================================================================ | ||
|
|
@@ -1681,7 +1647,7 @@ mod tests { | |
| fn test_u256_mul_overflow() { | ||
| let a = U256::new(0, 1); // 2^128 | ||
| let b = U256::new(0, 1); // 2^128 | ||
| // Product would be 2^256, which overflows. | ||
| // Product would be 2^256, which overflows. | ||
| assert_eq!(a.checked_mul(b), None); | ||
| } | ||
|
|
||
|
|
@@ -1883,15 +1849,9 @@ mod tests { | |
| #[test] | ||
| fn test_ceil_div_positive() { | ||
| // ceil(7 / 3) = 3 | ||
| assert_eq!( | ||
| ceil_div_positive_checked(U256::from_u128(7), U256::from_u128(3)), | ||
| U256::from_u128(3) | ||
| ); | ||
| assert_eq!(ceil_div_positive_checked(U256::from_u128(7), U256::from_u128(3)), U256::from_u128(3)); | ||
| // ceil(6 / 3) = 2 | ||
| assert_eq!( | ||
| ceil_div_positive_checked(U256::from_u128(6), U256::from_u128(3)), | ||
| U256::from_u128(2) | ||
| ); | ||
| assert_eq!(ceil_div_positive_checked(U256::from_u128(6), U256::from_u128(3)), U256::from_u128(2)); | ||
| } | ||
|
|
||
| // --- saturating_mul_u256_u64 --- | ||
|
|
@@ -2022,14 +1982,8 @@ mod tests { | |
| #[test] | ||
| fn test_mul_div_max() { | ||
| // MAX * 1 / 1 = MAX | ||
| assert_eq!( | ||
| mul_div_floor_u256(U256::MAX, U256::ONE, U256::ONE), | ||
| U256::MAX | ||
| ); | ||
| assert_eq!(mul_div_floor_u256(U256::MAX, U256::ONE, U256::ONE), U256::MAX); | ||
| // 1 * 1 / 1 = 1 | ||
| assert_eq!( | ||
| mul_div_floor_u256(U256::ONE, U256::ONE, U256::ONE), | ||
| U256::ONE | ||
| ); | ||
| assert_eq!(mul_div_floor_u256(U256::ONE, U256::ONE, U256::ONE), U256::ONE); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: dcccrypto/percolator
Length of output: 163
🏁 Script executed:
Repository: dcccrypto/percolator
Length of output: 945
🏁 Script executed:
Repository: dcccrypto/percolator
Length of output: 5050
🏁 Script executed:
Repository: dcccrypto/percolator
Length of output: 1594
🏁 Script executed:
Repository: dcccrypto/percolator
Length of output: 7551
Harness discovery silently skips
#[kani::should_panic]proofs.The
grep -A 3pattern captures the matched line plus 3 lines after it (4 lines total). Harnesses with four attributes—#[kani::proof],#[kani::unwind],#[kani::solver], and#[kani::should_panic]—place thefndeclaration at position +4, which is outside the capture range. This causes all config validation proofs (e.g.,proof_config_rejects_oversized_max_accounts,proof_config_rejects_invalid_bps) to be silently dropped from the audit run.Use an attribute-aware extraction that skips all
#[…]lines following#[kani::proof]:Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents