Skip to content

Commit

Permalink
add fallback test
Browse files Browse the repository at this point in the history
  • Loading branch information
nalinbhardwaj committed May 19, 2024
1 parent 4ad990d commit e671bd2
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
9 changes: 8 additions & 1 deletion src/P256.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pragma solidity 0.8.21;

/**
* Helper library for external contracts to verify P256 signatures.
* Tries to use RIP-7212 precompile if available on the chain, and if not falls
* back to more expensive Solidity implementation.
**/
library P256 {
address constant PRECOMPILE = address(0x100);
Expand All @@ -18,7 +20,12 @@ library P256 {
bytes memory args = abi.encode(message_hash, r, s, x, y);

(bool success, bytes memory ret) = PRECOMPILE.staticcall(args);
if (success && ret.length > 0) return abi.decode(ret, (uint256)) == 1;
if (success && ret.length > 0) {
// RIP-7212 precompile returns 1 if signature is valid
// and nothing if signature is invalid, so those fall back to
// more expensive Solidity implementation.
return abi.decode(ret, (uint256)) == 1;
}

(bool fallbackSuccess, bytes memory fallbackRet) = VERIFIER.staticcall(
args
Expand Down
59 changes: 59 additions & 0 deletions test/P256.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ import {stdJson} from "forge-std/StdJson.sol";
import {P256} from "../src/P256.sol";
import {P256Verifier} from "../src/P256Verifier.sol";

contract FakePrecompile {
fallback(bytes calldata input) external returns (bytes memory) {
(bool success, bytes memory ret) = P256.VERIFIER.staticcall(input);
assert(success);

uint256 retUint = abi.decode(ret, (uint256));
if (retUint == 1) {
return abi.encode(1);
} else {
return abi.encode();
}
}
}

contract P256Test is Test {
uint256[2] public pubKey;

Expand Down Expand Up @@ -57,4 +71,49 @@ contract P256Test is Test {
res = P256.verifySignature(hash, r, s, pubKey[0], pubKey[1]);
assertEq(res, true);
}

function testPrecompileUsage() public {
uint256 r = 0x01655c1753db6b61a9717e4ccc5d6c4bf7681623dd54c2d6babc55125756661c;
uint256 s = 7033802732221576339889804108463427183539365869906989872244893535944704590394;

bytes32 hash = 0x267f9ea080b54bbea2443dff8aa543604564329783b6a515c6663a691c555490;

uint gasBefore = gasleft();
bool res = P256.verifySignatureAllowMalleability(
hash,
r,
s,
pubKey[0],
pubKey[1]
);
assertEq(res, true);
uint gasUsedFallback = gasBefore - gasleft();
assert(gasUsedFallback > 100_000); // no precompile, used Solidity implementation

vm.etch(P256.PRECOMPILE, type(FakePrecompile).runtimeCode);

gasBefore = gasleft();
res = P256.verifySignatureAllowMalleability(
hash,
r,
s,
pubKey[0],
pubKey[1]
);
assertEq(res, true);
uint gasUsedPrecompile = gasBefore - gasleft();
assert(gasUsedPrecompile < gasUsedFallback); // precompile, used precompile

gasBefore = gasleft();
res = P256.verifySignatureAllowMalleability(
hash,
r + 1,
s,
pubKey[0],
pubKey[1]
);
assertEq(res, false);
uint gasUsedInvalidSignature = gasBefore - gasleft();
assert(gasUsedInvalidSignature > gasUsedFallback); // invalid signature, used precompile and failed, so fall back to Solidity implementation and failed with nearly double gas
}
}

0 comments on commit e671bd2

Please sign in to comment.