Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ECDSAMetaTransactionContext #290

Draft
wants to merge 53 commits into
base: msg-sender-trick
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
97cf3c7
add scaffold-contract task
ItsNickBarry Mar 19, 2025
619f183
fix scaffold interfaces
ItsNickBarry Mar 19, 2025
d438270
add EIP712Constants generator task
ItsNickBarry Mar 19, 2025
f6ff206
generate domain separator calculation functions
ItsNickBarry Mar 19, 2025
11b3cbb
use assembly for chainid and address lookup
ItsNickBarry Mar 19, 2025
136f3bc
remove assembly-sourced values from calculator parameters
ItsNickBarry Mar 19, 2025
4ea8f31
restrict calculator functions to pure where possible
ItsNickBarry Mar 19, 2025
72e3739
generate calculator comments
ItsNickBarry Mar 19, 2025
695cd94
generate EIP712Constants tests
ItsNickBarry Mar 19, 2025
2a0091d
use ejs for templating
ItsNickBarry Mar 20, 2025
0b0d185
remove template whitespace
ItsNickBarry Mar 20, 2025
02edad1
fix test name spelling
ItsNickBarry Mar 20, 2025
ab23b1e
mark ECDSA assembly as memory-safe
ItsNickBarry Mar 20, 2025
e5bff61
test FungibleTokenPermit nonces
ItsNickBarry Mar 20, 2025
cc7dfa2
add ECDSA#tryRecover functions using error function passing
ItsNickBarry Mar 20, 2025
c4f00c0
fix ECDSA comments
ItsNickBarry Mar 20, 2025
a1f871a
encapsulate error function passing within internally accessible funct…
ItsNickBarry Mar 20, 2025
d3dad19
test tryRecover functions
ItsNickBarry Mar 20, 2025
c71230a
fix whitespace and add comments to error wrapper functions
ItsNickBarry Mar 20, 2025
9d104ac
clean up generated EIP712 whitespace
ItsNickBarry Mar 20, 2025
3853ed6
overwrite EIP712 library with procedurally generated version
ItsNickBarry Mar 20, 2025
ea376f3
reference domain hash constants in domain separator calculation funct…
ItsNickBarry Mar 20, 2025
a6d7b71
Merge branch 'crypto-improvements' into ecdsa-context
ItsNickBarry Mar 20, 2025
c9c3e70
remove view restriction from context functions
ItsNickBarry Mar 20, 2025
66061bd
Merge branch 'msg-sender-trick' into ecdsa-context
ItsNickBarry Mar 20, 2025
be3f803
Merge branch 'msg-sender-trick' into ecdsa-context
ItsNickBarry Mar 20, 2025
670d4fc
Merge branch 'msg-sender-trick' into ecdsa-context
ItsNickBarry Mar 20, 2025
d36ebea
remove view restrictions from functions that call context functions
ItsNickBarry Mar 20, 2025
26137ac
set evmVerion to cancun
ItsNickBarry Mar 20, 2025
a7510d4
add draft ECDSAMetaTransactionContext contracts
ItsNickBarry Mar 20, 2025
00776de
Merge branch 'msg-sender-trick' into ecdsa-context
ItsNickBarry Mar 20, 2025
b185260
use non-reverting recover for ECDSA context
ItsNickBarry Mar 20, 2025
4d633a0
fix _eip712Domain output
ItsNickBarry Mar 20, 2025
b3ad2be
generate ERC5267 fields constants
ItsNickBarry Mar 20, 2025
a9bb1b8
test ERC5267 fields constants
ItsNickBarry Mar 20, 2025
06dc3d8
referene ERC5267 fields constant in FungibleTokenPermit
ItsNickBarry Mar 20, 2025
214fff9
Merge branch 'crypto-improvements' into ecdsa-context
ItsNickBarry Mar 20, 2025
5bef448
reference ERC5267 fields constant in ECDSAMetaTransactionContext
ItsNickBarry Mar 20, 2025
32e632d
fix hash struct procedure
ItsNickBarry Mar 20, 2025
86662f6
test ECDSAMetaTransactionContext
ItsNickBarry Mar 20, 2025
be59343
fix test nesting
ItsNickBarry Mar 20, 2025
054d056
use semi-standardized transient storage slot
ItsNickBarry Mar 20, 2025
6087a18
support msg.value in ECDSAMetaTransactionContext
ItsNickBarry Mar 20, 2025
e71e5e9
add failing nonce revert tests
ItsNickBarry Mar 20, 2025
d1169a5
test incorrect msg.value
ItsNickBarry Mar 20, 2025
c88d411
fix comment
ItsNickBarry Mar 20, 2025
5bba739
replae nonce in suffix with expected signer address
ItsNickBarry Mar 20, 2025
8db59a2
update comments
ItsNickBarry Mar 20, 2025
b7fb3e9
add toEIP712RecoverableHash utility function
ItsNickBarry Mar 20, 2025
c6954a3
rename function to toEthSignRecoverableHash and use assembly for hashing
ItsNickBarry Mar 20, 2025
a61388f
test toEIP712RecoverableHash
ItsNickBarry Mar 20, 2025
16328d2
Merge branch 'crypto-improvements' into ecdsa-context
ItsNickBarry Mar 20, 2025
3650961
use toEIP712RecoverableHash utiltity function in ECDSAMetaTransaction…
ItsNickBarry Mar 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/access/access_control/_AccessControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ abstract contract _AccessControl is _IAccessControl, _Context {
* @notice revert if sender does not have given role
* @param role role to query
*/
function _checkRole(bytes32 role) internal view virtual {
function _checkRole(bytes32 role) internal virtual {
_checkRole(role, _msgSender());
}

Expand Down
193 changes: 174 additions & 19 deletions contracts/cryptography/ECDSA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ library ECDSA {
error ECDSA__InvalidV();

/**
* @notice recover signer of hashed message from signature
* @notice recover signer of hashed message from signature, reverting on failure
* @param hash hashed data payload
* @param signature signed data payload
* @return signer recovered message signer
Expand All @@ -22,35 +22,107 @@ library ECDSA {
bytes32 hash,
bytes memory signature
) internal pure returns (address signer) {
if (signature.length != 65) revert ECDSA__InvalidSignatureLength();
function() pure errorFn;

(signer, errorFn) = _tryRecover(hash, signature);

if (signer == address(0)) errorFn();
}

/**
* @notice recover signer of hashed message from signature v, r, and s values, reverting on failure
* @param hash hashed data payload
* @param v signature "v" value
* @param r signature "r" value
* @param s signature "s" value
* @return signer recovered message signer
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address signer) {
function() pure errorFn;

(signer, errorFn) = _tryRecover(hash, v, r, s);

if (signer == address(0)) errorFn();
}

/**
* @notice attempt to recover signer of hashed message from signature
* @param hash hashed data payload
* @param signature signed data payload
* @return signer recovered message signer (zero address on recovery failure)
*/
function tryRecover(
bytes32 hash,
bytes memory signature
) internal pure returns (address signer) {
(signer, ) = _tryRecover(hash, signature);
}

/**
* @notice attempt to recover signer of hashed message from signature v, r, and s values
* @param hash hashed data payload
* @param v signature "v" value
* @param r signature "r" value
* @param s signature "s" value
* @return signer recovered message signer (zero address on recovery failure)
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address signer) {
(signer, ) = _tryRecover(hash, v, r, s);
}

/**
* @notice attempt to recover signer of hashed message from signature
* @param hash hashed data payload
* @param signature signed data payload
* @return signer recovered message signer (zero address on recovery failure)
* @return errorFn wrapper function around custom error revert
*/
function _tryRecover(
bytes32 hash,
bytes memory signature
) private pure returns (address signer, function() pure errorFn) {
if (signature.length != 65) {
return (address(0), _revert_ECDSA__InvalidSignatureLength);
}

bytes32 r;
bytes32 s;
uint8 v;

assembly {
assembly ('memory-safe') {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}

signer = recover(hash, v, r, s);
(signer, errorFn) = _tryRecover(hash, v, r, s);
}

/**
* @notice recover signer of hashed message from signature v, r, and s values
* @notice attempt to recover signer of hashed message from signature v, r, and s values
* @param hash hashed data payload
* @param v signature "v" value
* @param r signature "r" value
* @param s signature "s" value
* @return signer recovered message signer
* @return signer recovered message signer (zero address on recovery failure)
* @return errorFn wrapper function around custom error revert
*/
function recover(
function _tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address signer) {
) private pure returns (address signer, function() pure errorFn) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
Expand All @@ -63,24 +135,107 @@ library ECDSA {
if (
uint256(s) >
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
) revert ECDSA__InvalidS();
if (v != 27 && v != 28) revert ECDSA__InvalidV();
) return (address(0), _revert_ECDSA__InvalidS);

if (v != 27 && v != 28) return (address(0), _revert_ECDSA__InvalidV);

// If the signature is valid (and not malleable), return the signer address
signer = ecrecover(hash, v, r, s);
if (signer == address(0)) revert ECDSA__InvalidSignature();

if (signer == address(0))
return (address(0), _revert_ECDSA__InvalidSignature);
}

/**
* @notice generate an "Ethereum Signed Message" in the format returned by the eth_sign JSON-RPC method
* @param hash hashed data payload
* @return signedMessage signed message hash
* @param payloadHash hashed data payload
* @return recoverableHash hash to validate against signature via ECDSA function
*/
function toEthSignRecoverableHash(
bytes32 payloadHash
) internal pure returns (bytes32 recoverableHash) {
assembly {
// assembly block equivalent to:
//
// recoverableHash = keccak256(
// abi.encodePacked(
// '\x19Ethereum Signed Message:\n32',
// payloadHash
// )
// );

// load free memory pointer
let pointer := mload(64)

mstore(pointer, '\x19Ethereum Signed Message:\n32')
mstore(add(pointer, 28), payloadHash)

recoverableHash := keccak256(pointer, 60)
}
}

/**
* @notice generate an EIP712 signable hash of typed structured data
* @param domainSeparator EIP712 domain separator
* @param structHash hash of underlying signed data generated through the EIP712 "hashStruct" process
* @return recoverableHash hash to validate against signature via ECDSA function
*/
function toEIP712RecoverableHash(
bytes32 domainSeparator,
bytes32 structHash
) internal pure returns (bytes32 recoverableHash) {
assembly {
// assembly block equivalent to:
//
// recoverableHash = keccak256(
// abi.encodePacked(
// uint16(0x1901),
// domainSeparator,
// structHash
// )
// );

// load free memory pointer
let pointer := mload(64)

// this magic value is the EIP-191 signed data header, consisting of
// the hardcoded 0x19 and the one-byte version 0x01
mstore(
pointer,
0x1901000000000000000000000000000000000000000000000000000000000000
)
mstore(add(pointer, 2), domainSeparator)
mstore(add(pointer, 34), structHash)

recoverableHash := keccak256(pointer, 66)
}
}

/**
* @notice wrapper function for passing custom errors internally
*/
function _revert_ECDSA__InvalidS() private pure {
revert ECDSA__InvalidS();
}

/**
* @notice wrapper function for passing custom errors internally
*/
function _revert_ECDSA__InvalidSignature() private pure {
revert ECDSA__InvalidSignature();
}

/**
* @notice wrapper function for passing custom errors internally
*/
function _revert_ECDSA__InvalidSignatureLength() private pure {
revert ECDSA__InvalidSignatureLength();
}

/**
* @notice wrapper function for passing custom errors internally
*/
function toEthSignedMessageHash(
bytes32 hash
) internal pure returns (bytes32 signedMessage) {
signedMessage = keccak256(
abi.encodePacked('\x19Ethereum Signed Message:\n32', hash)
);
function _revert_ECDSA__InvalidV() private pure {
revert ECDSA__InvalidV();
}
}
Loading