From f9a1a0a5cda2f5a147a30bc87ed7c01b49ea0ee2 Mon Sep 17 00:00:00 2001 From: lefterislazar Date: Tue, 11 Aug 2026 13:41:57 +0000 Subject: [PATCH] Schoolbook division revert fix --- src/modexp/LimbMath.sol | 5 ++++- test/modexp/ModexpDeployed.t.sol | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/modexp/LimbMath.sol b/src/modexp/LimbMath.sol index 4aec71a..037d48d 100644 --- a/src/modexp/LimbMath.sol +++ b/src/modexp/LimbMath.sol @@ -300,7 +300,10 @@ library LimbMath { bool doRefinement; if (uHi >= vTop) { qHat = type(uint256).max; - rHat = uLo + vTop; + // Algorithm D uses the wrapped sum below to detect carry in doRefinement. + unchecked { + rHat = uLo + vTop; + } doRefinement = (rHat >= uLo); } else { (qHat, rHat) = div512by256(uHi, uLo, vTop); diff --git a/test/modexp/ModexpDeployed.t.sol b/test/modexp/ModexpDeployed.t.sol index 72c66c8..4b74739 100644 --- a/test/modexp/ModexpDeployed.t.sol +++ b/test/modexp/ModexpDeployed.t.sol @@ -93,6 +93,21 @@ contract ModexpDeployedTest is Test { assertEq(outDep, outPre); } + function test_identical_schoolbook_saturated_estimate_wrap() public view { + uint256 halfWord = uint256(1) << 255; + bytes memory input = _encodeInput( + abi.encodePacked(bytes32(halfWord), bytes32(halfWord), bytes32(0)), + hex"01", + abi.encodePacked(bytes32(halfWord), bytes32(halfWord + 1)) + ); + + (bool okPre, bytes memory outPre) = _callPrecompile(input); + (bool okDep, bytes memory outDep) = _callDeployed(input); + assertTrue(okPre); + assertTrue(okDep); + assertEq(outDep, outPre); + } + function test_identical_even_modulus() public view { // 3^7 mod 10 bytes memory input = _encodeInput(