Skip to content

Commit

Permalink
Add isValidERC6492SignatureNow
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized committed May 27, 2024
1 parent fdf506e commit 7e72fec
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 35 deletions.
37 changes: 19 additions & 18 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ ERC1155Test:testSafeTransferFromToZeroReverts(uint256) (runs: 278, μ: 71146, ~:
ERC1155Test:test__codesize() (gas: 42341)
ERC1271Test:testBasefeeBytecodeContract() (gas: 45430)
ERC1271Test:testIsValidSignature() (gas: 2464705)
ERC1271Test:testIsValidSignature(uint256) (runs: 278, μ: 255853, ~: 215142)
ERC1271Test:testIsValidSignature(uint256) (runs: 278, μ: 271230, ~: 217334)
ERC1271Test:test__codesize() (gas: 30392)
ERC1967FactoryTest:testChangeAdmin() (gas: 266356)
ERC1967FactoryTest:testChangeAdminUnauthorized() (gas: 257653)
Expand Down Expand Up @@ -229,21 +229,21 @@ ERC2981Test:testRoyaltyOverflowCheckDifferential(uint256,uint256) (runs: 278, μ
ERC2981Test:testSetAndGetRoyaltyInfo(uint256) (runs: 278, μ: 109506, ~: 105295)
ERC2981Test:test__codesize() (gas: 7113)
ERC4337FactoryTest:testCreateAccountRepeatedDeployment() (gas: 149757)
ERC4337FactoryTest:testCreateAccountRepeatedDeployment(uint256) (runs: 278, μ: 171716, ~: 171732)
ERC4337FactoryTest:testDeployDeterministic(uint256) (runs: 278, μ: 128746, ~: 139893)
ERC4337FactoryTest:testCreateAccountRepeatedDeployment(uint256) (runs: 278, μ: 171717, ~: 171726)
ERC4337FactoryTest:testDeployDeterministic(uint256) (runs: 278, μ: 130908, ~: 139893)
ERC4337FactoryTest:test__codesize() (gas: 12786)
ERC4337Test:testCdFallback() (gas: 438129)
ERC4337Test:testCdFallback2() (gas: 1135369)
ERC4337Test:testDelegateExecute() (gas: 366495)
ERC4337Test:testDelegateExecute(uint256) (runs: 278, μ: 351909, ~: 345338)
ERC4337Test:testDelegateExecute(uint256) (runs: 278, μ: 352066, ~: 345338)
ERC4337Test:testDelegateExecuteRevertsIfOwnerSlotValueChanged() (gas: 320155)
ERC4337Test:testDepositFunctions() (gas: 497250)
ERC4337Test:testDirectStorage() (gas: 70435)
ERC4337Test:testDisableInitializerForImplementation() (gas: 1320997)
ERC4337Test:testETHReceived() (gas: 16584)
ERC4337Test:testExecute() (gas: 383602)
ERC4337Test:testExecuteBatch() (gas: 694209)
ERC4337Test:testExecuteBatch(uint256) (runs: 278, μ: 529727, ~: 670089)
ERC4337Test:testExecuteBatch(uint256) (runs: 278, μ: 531944, ~: 670391)
ERC4337Test:testInitializer() (gas: 285472)
ERC4337Test:testIsValidSignature() (gas: 120064)
ERC4337Test:testIsValidSignaturePersonalSign() (gas: 103314)
Expand Down Expand Up @@ -276,21 +276,21 @@ ERC4626Test:testWithdrawZero() (gas: 51874)
ERC4626Test:test__codesize() (gas: 37175)
ERC6551Test:testBaseFeeMini() (gas: 39514)
ERC6551Test:testCdFallback() (gas: 895599)
ERC6551Test:testDeployERC6551(uint256) (runs: 278, μ: 170617, ~: 168897)
ERC6551Test:testDeployERC6551(uint256) (runs: 278, μ: 170192, ~: 168886)
ERC6551Test:testDeployERC6551Proxy() (gas: 80395)
ERC6551Test:testExecute() (gas: 507518)
ERC6551Test:testExecuteBatch() (gas: 817605)
ERC6551Test:testExecuteBatch(uint256) (runs: 278, μ: 642316, ~: 762990)
ERC6551Test:testExecuteBatch(uint256) (runs: 278, μ: 675385, ~: 768935)
ERC6551Test:testInitializeERC6551ProxyImplementation() (gas: 189914)
ERC6551Test:testIsValidSigner(address) (runs: 278, μ: 167478, ~: 167467)
ERC6551Test:testIsValidSigner(address) (runs: 278, μ: 167466, ~: 167467)
ERC6551Test:testOnERC1155BatchReceived() (gas: 1702522)
ERC6551Test:testOnERC1155Received() (gas: 1699885)
ERC6551Test:testOnERC721Received() (gas: 1738813)
ERC6551Test:testOnERC721ReceivedCycles() (gas: 1728089)
ERC6551Test:testOnERC721ReceivedCyclesWithDifferentChainIds(uint256) (runs: 278, μ: 450900, ~: 455396)
ERC6551Test:testOwnerWorksWithChainIdChange(uint256,uint256) (runs: 278, μ: 1364062, ~: 1364068)
ERC6551Test:testOnERC721ReceivedCyclesWithDifferentChainIds(uint256) (runs: 278, μ: 450551, ~: 455368)
ERC6551Test:testOwnerWorksWithChainIdChange(uint256,uint256) (runs: 278, μ: 1364065, ~: 1364075)
ERC6551Test:testSupportsInterface() (gas: 169450)
ERC6551Test:testUpdateState(uint256) (runs: 278, μ: 235207, ~: 235130)
ERC6551Test:testUpdateState(uint256) (runs: 278, μ: 235225, ~: 235137)
ERC6551Test:testUpgrade() (gas: 248450)
ERC6551Test:test__codesize() (gas: 51546)
ERC6909Test:testApprove() (gas: 36872)
Expand Down Expand Up @@ -1153,23 +1153,24 @@ SafeTransferLibTest:testTryTransferETH() (gas: 148701)
SafeTransferLibTest:testTryTransferETHWithNoGrief() (gas: 537025)
SafeTransferLibTest:testTryTransferETHWithNoStorageWrites() (gas: 192444)
SafeTransferLibTest:test__codesize() (gas: 67852)
SignatureCheckerLibTest:testERC6492AllowSideEffectsPostDeploy() (gas: 444936)
SignatureCheckerLibTest:testERC6492AllowSideEffectsPreDeploy() (gas: 451030)
SignatureCheckerLibTest:testERC6492AllowSideEffectsPostDeploy() (gas: 444959)
SignatureCheckerLibTest:testERC6492AllowSideEffectsPreDeploy() (gas: 450733)
SignatureCheckerLibTest:testERC6492PreDeploy() (gas: 1233291)
SignatureCheckerLibTest:testEmptyCalldataHelpers() (gas: 3920)
SignatureCheckerLibTest:testSignatureChecker(bytes32) (runs: 278, μ: 58149, ~: 58181)
SignatureCheckerLibTest:testSignatureChecker(bytes32) (runs: 278, μ: 58039, ~: 58052)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithInvalidSignature() (gas: 21287)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithInvalidSigner() (gas: 30742)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithMatchingSignerAndSignature() (gas: 17685)
SignatureCheckerLibTest:testSignatureCheckerOnEOAWithWrongSignedMessageHash() (gas: 21244)
SignatureCheckerLibTest:testSignatureCheckerOnMaliciousWallet() (gas: 31740)
SignatureCheckerLibTest:testSignatureCheckerOnMaliciousWallet() (gas: 31805)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithInvalidSignature() (gas: 77467)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithInvalidSigner() (gas: 25705)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithMatchingSignerAndSignature() (gas: 64515)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithMatchingSignerAndSignature() (gas: 64493)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithWrongSignedMessageHash() (gas: 64572)
SignatureCheckerLibTest:testSignatureCheckerOnWalletWithZeroAddressSigner() (gas: 12340)
SignatureCheckerLibTest:testToEthSignedMessageHashDifferential(bytes) (runs: 278, μ: 1285, ~: 1284)
SignatureCheckerLibTest:testToEthSignedMessageHashDifferential(bytes32) (runs: 278, μ: 481, ~: 481)
SignatureCheckerLibTest:test__codesize() (gas: 13644)
SignatureCheckerLibTest:test__codesize() (gas: 14749)
SoladyTest:test__codesize() (gas: 840)
TestPlus:test__codesize() (gas: 406)
UUPSUpgradeableTest:testNotDelegatedGuard() (gas: 15875)
Expand All @@ -1190,7 +1191,7 @@ UpgradeableBeaconTest:testUpgradeableSolidityBeaconOnlyOwnerFunctions() (gas: 26
UpgradeableBeaconTest:testUpgradeableYulBeaconOnlyFnSelectorNotRecognised() (gas: 172796)
UpgradeableBeaconTest:testUpgradeableYulBeaconOnlyOwnerFunctions() (gas: 198398)
UpgradeableBeaconTest:test__codesize() (gas: 8808)
WETHInvariants:invariantTotalSupplyEqualsBalance() (runs: 256, calls: 3840, reverts: 1890)
WETHInvariants:invariantTotalSupplyEqualsBalance() (runs: 256, calls: 3840, reverts: 1889)
WETHInvariants:test__codesize() (gas: 5264)
WETHTest:testDeposit() (gas: 68090)
WETHTest:testDeposit(uint256) (runs: 278, μ: 67784, ~: 68384)
Expand Down
90 changes: 73 additions & 17 deletions src/utils/SignatureCheckerLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ library SignatureCheckerLib {
// Note: The ERC6492 operations do NOT have an ECDSA fallback.
// These functions are intended to be used with the regular `isValidSignatureNow` functions
// or other signature verification functions (e.g. P256).
// The calldata variants are excluded for brevity.

/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt to unwrap
Expand All @@ -521,25 +522,13 @@ library SignatureCheckerLib {
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n_ := add(0x20, mload(signature_))
pop(staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_))
// forgefmt: disable-next-item
_isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d_), f_),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer_, // The `signer` address.
m_, // Offset of calldata in memory.
add(returndatasize(), 0x44), // Length of calldata in memory.
d_, // Offset of returndata.
0x20 // Length of returndata to write.
_isValid :=
and(
eq(mload(d_), f_),
staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
)
)
}
for {} 1 {} {
if iszero(
Expand All @@ -551,7 +540,7 @@ library SignatureCheckerLib {
isValid := callIsValidSignature(signer, hash, signature)
break
}
if iszero(gt(mload(signature), 0x5f)) { break }
if iszero(gt(mload(signature), 0x7f)) { break } // Signature too short.
if iszero(extcodesize(signer)) { if iszero(deployOrPrepare(signature)) { break } }
let innerSignature := add(add(signature, 0x20), mload(add(signature, 0x60)))
isValid := callIsValidSignature(signer, hash, innerSignature)
Expand All @@ -564,6 +553,73 @@ library SignatureCheckerLib {
}
}

/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt to unwrap
/// the signature and deploy the smart account before doing a regular ERC1271 check.
/// Note: This function requires the reverting verifier to be deployed.
/// See: https://gist.github.com/Vectorized/846a474c855eee9e441506676800a9ad
function isValidERC6492SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
function verifyWithRevert(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
mstore(m_, signer_)
mstore(add(m_, 0x20), hash_)
_isValid :=
call(
gas(), // Remaining gas.
0x00000000009F9FB8Ee7F67678D343BC19A1c0adc, // Reverting verifier.
0, // Send zero ETH.
m_, // Start of memory.
add(returndatasize(), 0x40), // Length of calldata in memory.
// forgefmt: disable-next-item
staticcall(gas(), 4, add(signature_, 0x20), mload(signature_),
add(m_, 0x40), mload(signature_)),
0x00 // Length of returndata to write.
)
_isValid := gt(returndatasize(), _isValid)
}
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
pop(staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_))
_isValid :=
and(
eq(mload(d_), f_),
staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
)
}
for {} 1 {} {
if iszero(
eq(
mload(add(signature, mload(signature))),
mul(0x6492, div(not(mload(0x60)), 0xffff)) // `0x6492...6492`.
)
) {
isValid := callIsValidSignature(signer, hash, signature)
break
}
if iszero(gt(mload(signature), 0x7f)) { break } // Signature too short.
if iszero(extcodesize(signer)) {
isValid := verifyWithRevert(signer, hash, signature)
break
}
let innerSignature := add(add(signature, 0x20), mload(add(signature, 0x60)))
isValid := callIsValidSignature(signer, hash, innerSignature)
if iszero(isValid) { isValid := verifyWithRevert(signer, hash, signature) }
break
}
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down
42 changes: 42 additions & 0 deletions test/SignatureCheckerLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ contract SignatureCheckerLibTest is SoladyTest {
bytes innerSignature;
bytes signature;
bool result;
address revertingVerifier;
}

function _erc6492TestTemps() internal returns (_ERC6492TestTemps memory t) {
Expand Down Expand Up @@ -455,4 +456,45 @@ contract SignatureCheckerLibTest is SoladyTest {
assertTrue(t.result);
assertEq(MockERC1271Wallet(t.smartAccount).signer(), t.eoa);
}

function _etchERC6492RevertingVerifier() internal returns (address revertingVerifier) {
bytes memory initcode =
hex"605a80600a3d393df3fe3660403d373d3d3d906020918251805190843d9101903d515af19082608460405180519081850190604001606037826080820152631626ba7e8352833584526040805201601c82355afa91630b135d3f60e11b905114161638fd";
address factory = _etchNicksFactory();
bytes32 salt = 0x000000000000000000000000000000000000000078c7347000e4ac02b79ffe36;
(bool success,) = factory.call(abi.encodePacked(salt, initcode));
revertingVerifier = LibClone.predictDeterministicAddress(keccak256(initcode), salt, factory);
assertTrue(success);
assertGt(revertingVerifier.code.length, 0);
emit LogBytes32(keccak256(initcode));
emit LogBytes(revertingVerifier.code);
}

function testERC6492PreDeploy() public {
_ERC6492TestTemps memory t = _erc6492TestTemps();
t.revertingVerifier = _etchERC6492RevertingVerifier();

t.result = SignatureCheckerLib.isValidERC6492SignatureNow(
t.smartAccount, t.digest, t.innerSignature
);
assertFalse(t.result);
// This should return false, as the function does NOT do ECDSA fallback.
t.result = SignatureCheckerLib.isValidERC6492SignatureNow(t.eoa, t.digest, t.innerSignature);
assertFalse(t.result);
assertEq(t.smartAccount.code.length, 0);
t.result =
SignatureCheckerLib.isValidERC6492SignatureNow(t.smartAccount, t.digest, t.signature);
assertTrue(t.result);
assertEq(t.smartAccount.code.length, 0);
t.result =
SignatureCheckerLib.isValidERC6492SignatureNow(t.smartAccount, t.digest, t.signature);
assertTrue(t.result);
assertEq(t.smartAccount.code.length, 0);

t.result = SignatureCheckerLib.isValidERC6492SignatureNow(
t.smartAccount, keccak256(""), t.signature
);
assertFalse(t.result);
assertEq(t.smartAccount.code.length, 0);
}
}

0 comments on commit 7e72fec

Please sign in to comment.