Skip to content

Commit dd9604c

Browse files
authored
remove internal precompile call (#3)
1 parent 221b98b commit dd9604c

3 files changed

Lines changed: 268 additions & 123 deletions

File tree

src/modexp/Modexp.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ library Modexp {
1818
bytes memory base,
1919
bytes memory exponent,
2020
bytes memory modulus
21-
) internal view returns (bytes memory result) {
21+
) internal pure returns (bytes memory result) {
2222
if (modulus.length == 0) return new bytes(0);
2323

2424
// Check if modulus is odd (last byte has bit 0 set)

src/modexp/ModexpBarrett.sol

Lines changed: 40 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ library ModexpBarrett {
1212
bytes memory base,
1313
bytes memory exponent,
1414
bytes memory modulus
15-
) internal view returns (bytes memory result) {
15+
) internal pure returns (bytes memory result) {
1616
uint256 modLen = modulus.length;
1717
if (modLen == 0) return new bytes(0);
1818

@@ -46,10 +46,10 @@ library ModexpBarrett {
4646
uint256 k = (modLen + 31) / 32;
4747

4848
uint256[] memory n = _bytesToLimbs(modulus, k);
49-
uint256[] memory a = _reduceBase(base, modulus, k);
49+
uint256[] memory a = _reduceBase(base, n, k);
5050

5151
// Barrett constant: mu = floor(2^(512k) / n), has k+1 limbs
52-
uint256[] memory mu = _computeBarrettConstant(n, k, modulus);
52+
uint256[] memory mu = _computeBarrettConstant(n, k);
5353

5454
// one = 1 as k-limb number (used as initial accumulator)
5555
uint256[] memory r = new uint256[](k);
@@ -144,83 +144,31 @@ library ModexpBarrett {
144144
}
145145
}
146146

147-
// ── Precompile wrapper ────────────────────────────────────────────
148-
149-
function _callPrecompile(bytes memory b, bytes memory e, bytes memory m)
150-
private view returns (bytes memory result)
151-
{
152-
uint256 modLen = m.length;
153-
result = new bytes(modLen);
154-
bytes memory input = abi.encodePacked(
155-
uint256(b.length), uint256(e.length), uint256(modLen), b, e, m
156-
);
157-
assembly {
158-
let ok := staticcall(gas(), 0x05, add(input, 0x20), mload(input), add(result, 0x20), modLen)
159-
if iszero(ok) { revert(0, 0) }
160-
}
161-
}
162-
163-
function _reduceBase(bytes memory base, bytes memory modulus, uint256 k)
164-
private view returns (uint256[] memory)
147+
/// @dev Reduces base mod n via schoolbook division remainder.
148+
function _reduceBase(bytes memory base, uint256[] memory n, uint256 k)
149+
private pure returns (uint256[] memory)
165150
{
166-
return _bytesToLimbs(_callPrecompile(base, hex"01", modulus), k);
167-
}
168-
169-
function _uint256ToMinBytes(uint256 val) private pure returns (bytes memory) {
170-
if (val == 0) return hex"00";
171-
uint256 byteLen = 0;
172-
uint256 tmp = val;
173-
while (tmp > 0) {
174-
byteLen++;
175-
tmp >>= 8;
176-
}
177-
bytes memory result = new bytes(byteLen);
178-
unchecked {
179-
for (uint256 i = 0; i < byteLen; i++) {
180-
result[byteLen - 1 - i] = bytes1(uint8(val));
181-
val >>= 8;
182-
}
183-
}
184-
return result;
151+
uint256 baseLen = base.length;
152+
if (baseLen == 0) return new uint256[](k);
153+
uint256 baseK = (baseLen + 31) / 32;
154+
if (baseK < k) baseK = k;
155+
uint256[] memory baseLimbs = _bytesToLimbs(base, baseK);
156+
(, uint256[] memory rem) = _schoolbookDiv(baseLimbs, baseK, n, k);
157+
return rem;
185158
}
186159

187160
// ── Barrett constant computation ──────────────────────────────────
188161

189162
/// @dev Computes mu = floor(2^(512k) / n).
190-
function _computeBarrettConstant(uint256[] memory n, uint256 k, bytes memory modulus)
191-
private view returns (uint256[] memory)
163+
function _computeBarrettConstant(uint256[] memory n, uint256 k)
164+
private pure returns (uint256[] memory)
192165
{
193-
// r = 2^(512k) mod n via precompile
194-
uint256 expVal = 512 * k;
195-
bytes memory expBytes = _uint256ToMinBytes(expVal);
196-
bytes memory base2 = hex"02";
197-
uint256[] memory r = _bytesToLimbs(_callPrecompile(base2, expBytes, modulus), k);
198-
199-
// dividend = 2^(512k) - r, which is exactly divisible by n
200-
// dividend has 2k+1 limbs: limbs 0..k-1 = two's complement of r, limb 2k = 1
166+
// Construct 2^(512k) as a (2k+1)-limb number: all zeros except limb[2k] = 1
201167
uint256 dLen = 2 * k + 1;
202168
uint256[] memory dividend = new uint256[](dLen);
203-
204-
// Compute -r mod 2^(256k), i.e., two's complement
205-
assembly {
206-
let divP := add(dividend, 0x20)
207-
let rP := add(r, 0x20)
208-
let borrow := 0
209-
for { let i := 0 } lt(i, k) { i := add(i, 1) } {
210-
let ri := mload(add(rP, mul(i, 0x20)))
211-
let d := sub(sub(0, ri), borrow)
212-
borrow := or(gt(ri, 0), and(iszero(ri), borrow))
213-
mstore(add(divP, mul(i, 0x20)), d)
214-
}
215-
for { let i := k } lt(i, mul(2, k)) { i := add(i, 1) } {
216-
let d := sub(0, borrow)
217-
mstore(add(divP, mul(i, 0x20)), d)
218-
}
219-
mstore(add(divP, mul(mul(2, k), 0x20)), sub(1, borrow))
220-
}
221-
222-
// mu = dividend / n via schoolbook division
223-
return _schoolbookDiv(dividend, dLen, n, k);
169+
dividend[2 * k] = 1;
170+
(uint256[] memory mu,) = _schoolbookDiv(dividend, dLen, n, k);
171+
return mu;
224172
}
225173

226174
// ── Division helpers ──────────────────────────────────────────────
@@ -277,15 +225,17 @@ library ModexpBarrett {
277225
uint256 dLen,
278226
uint256[] memory divisor,
279227
uint256 k
280-
) private pure returns (uint256[] memory quotient) {
228+
) private pure returns (uint256[] memory quotient, uint256[] memory rem) {
229+
rem = new uint256[](k);
230+
281231
// Find actual length of dividend (strip leading zero limbs)
282232
uint256 m = dLen;
283233
while (m > 0 && dividend[m - 1] == 0) {
284234
m--;
285235
}
286236
if (m == 0) {
287237
quotient = new uint256[](1);
288-
return quotient;
238+
return (quotient, rem);
289239
}
290240

291241
// Find effective number of significant limbs in divisor
@@ -305,13 +255,15 @@ library ModexpBarrett {
305255
(q, remainder) = _div512by256(remainder, dividend[i], d);
306256
quotient[i] = q;
307257
}
308-
return quotient;
258+
rem[0] = remainder;
259+
return (quotient, rem);
309260
}
310261

311262
// Multi-limb divisor: Knuth Algorithm D (using kEff significant limbs)
312263
if (m < kEff) {
313264
quotient = new uint256[](1);
314-
return quotient;
265+
for (uint256 i = 0; i < m; i++) rem[i] = dividend[i];
266+
return (quotient, rem);
315267
}
316268
uint256 numQlimbs = m - kEff + 1;
317269
quotient = new uint256[](numQlimbs);
@@ -454,7 +406,19 @@ library ModexpBarrett {
454406
quotient[jj] = qHat;
455407
}
456408

457-
return quotient;
409+
// Extract remainder from u[0..kEff-1] and denormalize (shift right)
410+
if (shift > 0) {
411+
for (uint256 i = 0; i < kEff; i++) {
412+
rem[i] = u[i] >> shift;
413+
if (i + 1 < kEff) {
414+
rem[i] |= u[i + 1] << (256 - shift);
415+
}
416+
}
417+
} else {
418+
for (uint256 i = 0; i < kEff; i++) {
419+
rem[i] = u[i];
420+
}
421+
}
458422
}
459423

460424
// ── Schoolbook multiplication ─────────────────────────────────────

0 commit comments

Comments
 (0)