Add Calldata variants of ECDSA.recover, ECDSA.tryRecover and SignatureChecker.isValidSignatureNow (#5788)

This commit is contained in:
Hadrien Croubois
2025-07-11 16:57:04 +02:00
committed by GitHub
parent 667bb9b5c3
commit bc8f775df2
6 changed files with 114 additions and 8 deletions

View File

@ -74,6 +74,30 @@ library ECDSA {
}
}
/**
* @dev Variant of {tryRecover} that takes a signature in calldata
*/
function tryRecoverCalldata(
bytes32 hash,
bytes calldata signature
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, calldata slices would work here, but are
// significantly more expensive (length check) than using calldataload in assembly.
assembly ("memory-safe") {
r := calldataload(signature.offset)
s := calldataload(add(signature.offset, 0x20))
v := byte(0, calldataload(add(signature.offset, 0x40)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
@ -94,6 +118,15 @@ library ECDSA {
return recovered;
}
/**
* @dev Variant of {recover} that takes a signature in calldata
*/
function recoverCalldata(bytes32 hash, bytes calldata signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecoverCalldata(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*

View File

@ -38,6 +38,22 @@ library SignatureChecker {
}
}
/**
* @dev Variant of {isValidSignatureNow} that takes a signature in calldata
*/
function isValidSignatureNowCalldata(
address signer,
bytes32 hash,
bytes calldata signature
) internal view returns (bool) {
if (signer.code.length == 0) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecoverCalldata(hash, signature);
return err == ECDSA.RecoverError.NoError && recovered == signer;
} else {
return isValidERC1271SignatureNow(signer, hash, signature);
}
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC-1271.
@ -49,13 +65,26 @@ library SignatureChecker {
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
) internal view returns (bool result) {
bytes4 selector = IERC1271.isValidSignature.selector;
uint256 length = signature.length;
assembly ("memory-safe") {
// Encoded calldata is :
// [ 0x00 - 0x03 ] <selector>
// [ 0x04 - 0x23 ] <hash>
// [ 0x24 - 0x44 ] <signature offset> (0x40)
// [ 0x44 - 0x64 ] <signature length>
// [ 0x64 - ... ] <signature data>
let ptr := mload(0x40)
mstore(ptr, selector)
mstore(add(ptr, 0x04), hash)
mstore(add(ptr, 0x24), 0x40)
mcopy(add(ptr, 0x44), signature, add(length, 0x20))
let success := staticcall(gas(), signer, ptr, add(length, 0x64), 0, 0x20)
result := and(success, and(gt(returndatasize(), 0x19), eq(mload(0x00), selector)))
}
}
/**