Commit 3b1a490f authored by lbeder's avatar lbeder Committed by GitHub

EAS v1.2.0: gas optimizations, minor improvements and fixes, and some typo and...

EAS v1.2.0: gas optimizations, minor improvements and fixes, and some typo and NATSPEC fixes (#7094)

* Add an index and the schema record data to the Registered event

* Gas Optimization: Cache resolver.isPayable() external call

* Add extra multiRequest.data.length == 0 validation

* Gas Gas Optimization: Cache Revoked event arguments

* Make sure to refund the attester/revoker in case of non-last attestation/revocation and where there is no resolver

* Gas Optimization: cache loop iteration lengths

* Gas Optimization: optimize _resolveAttestation() and _resolveAttestations()

* Fix missing Natspec, renaming variables, and incorrect comments

* Add explicit deadline to delegated attestation/revocation requests

* Provide an option for users to invalidate nonces by increasing their nonces to (higher) new values

* Add extra input length validation in multiRevoke and multiAttest functions

* Updates sample usage in comments

* Capture the payment amount in signatures

* Bump version

* Emit NonceIncreased event when users increase their nonces

* Make delegate attestation deadline inclusive + handle prevent nonce overflow due to user error

* Minor fix

* Update bindings

* Fix lint

* Lock semver

* Add an index and the schema record data to the Registered event

* Gas Optimization: Cache resolver.isPayable() external call

* Add extra multiRequest.data.length == 0 validation

* Gas Gas Optimization: Cache Revoked event arguments

* Make sure to refund the attester/revoker in case of non-last attestation/revocation and where there is no resolver

* Gas Optimization: cache loop iteration lengths

* Gas Optimization: optimize _resolveAttestation() and _resolveAttestations()

* Fix missing Natspec, renaming variables, and incorrect comments

* Add explicit deadline to delegated attestation/revocation requests

* Provide an option for users to invalidate nonces by increasing their nonces to (higher) new values

* Add extra input length validation in multiRevoke and multiAttest functions

* Updates sample usage in comments

* Capture the payment amount in signatures

* Bump version

* Emit NonceIncreased event when users increase their nonces

* Make delegate attestation deadline inclusive + handle prevent nonce overflow due to user error

* Minor fix

* Update bindings

* Fix lint

* Lock semver
parent b73b7c21
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"src/EAS/EAS.sol": "0x00862a9f0088230acc1f5c5d0e4041bcc28cb3b3675d0eb7e1cceee7cf9502f8",
"src/EAS/SchemaRegistry.sol": "0xf1cd4415f85775124c226e1a356d8b9b5126b9e9bdbe5aebb3876d46f8e1217a",
"src/EAS/EAS.sol": "0x1acb25751a1206eb859cc5fcf934da2f84cfb907b8e8951d86fc4e43c53a7303",
"src/EAS/SchemaRegistry.sol": "0x305f3afed2e337cd70aac70fc202e6503b947b0a31e0d4e18c49486eeb635bb5",
"src/L1/L1CrossDomainMessenger.sol": "0x0e663b5d608b07cf278b94b1eeb3202abc01bea6b5905a3869010353df33ad1a",
"src/L1/L1ERC721Bridge.sol": "0xbb10b777d1cd36ef98b53df6675f37a20b14a9a82b174f0d8f8872eedca65f17",
"src/L1/L1StandardBridge.sol": "0xbd7b303cefe46bc14bf1a2b81e5702ff45ce9c5257524e59778e11c75f7f5bdc",
......
......@@ -8,6 +8,7 @@ bytes32 constant EMPTY_UID = 0;
uint64 constant NO_EXPIRATION_TIME = 0;
error AccessDenied();
error DeadlineExpired();
error InvalidEAS();
error InvalidLength();
error InvalidSignature();
......
This diff is collapsed.
......@@ -27,6 +27,7 @@ struct DelegatedAttestationRequest {
AttestationRequestData data; // The arguments of the attestation request.
Signature signature; // The ECDSA signature data.
address attester; // The attesting account.
uint64 deadline; // The deadline of the signature/request.
}
/// @dev A struct representing the full arguments of the multi attestation request.
......@@ -42,6 +43,7 @@ struct MultiDelegatedAttestationRequest {
Signature[] signatures; // The ECDSA signatures data. Please note that the signatures are assumed to be signed with
// increasing nonces.
address attester; // The attesting account.
uint64 deadline; // The deadline of the signature/request.
}
/// @dev A struct representing the arguments of the revocation request.
......@@ -63,6 +65,7 @@ struct DelegatedRevocationRequest {
RevocationRequestData data; // The arguments of the revocation request.
Signature signature; // The ECDSA signature data.
address revoker; // The revoking account.
uint64 deadline; // The deadline of the signature/request.
}
/// @dev A struct representing the full arguments of the multi revocation request.
......@@ -78,6 +81,7 @@ struct MultiDelegatedRevocationRequest {
Signature[] signatures; // The ECDSA signatures data. Please note that the signatures are assumed to be signed with
// increasing nonces.
address revoker; // The revoking account.
uint64 deadline; // The deadline of the signature/request.
}
/// @title IEAS
......@@ -87,15 +91,15 @@ interface IEAS {
/// @param recipient The recipient of the attestation.
/// @param attester The attesting account.
/// @param uid The UID the revoked attestation.
/// @param schema The UID of the schema.
event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schema);
/// @param schemaUID The UID of the schema.
event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID);
/// @dev Emitted when an attestation has been revoked.
/// @param recipient The recipient of the attestation.
/// @param attester The attesting account.
/// @param schema The UID of the schema.
/// @param schemaUID The UID of the schema.
/// @param uid The UID the revoked attestation.
event Revoked(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schema);
event Revoked(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID);
/// @dev Emitted when a data has been timestamped.
/// @param data The data.
......@@ -151,7 +155,8 @@ interface IEAS {
/// r: '0x148c...b25b',
/// s: '0x5a72...be22'
/// },
/// attester: '0xc5E8740aD971409492b1A63Db8d83025e0Fc427e'
/// attester: '0xc5E8740aD971409492b1A63Db8d83025e0Fc427e',
/// deadline: 1673891048
/// })
///
/// @param delegatedRequest The arguments of the delegated attestation request.
......@@ -236,7 +241,8 @@ interface IEAS {
/// r: '0x487s...67bb',
/// s: '0x12ad...2366'
/// }],
/// attester: '0x1D86495b2A7B524D747d2839b3C645Bed32e8CF4'
/// attester: '0x1D86495b2A7B524D747d2839b3C645Bed32e8CF4',
/// deadline: 1673891048
/// }])
///
/// @param multiDelegatedRequests The arguments of the delegated multi attestation requests. The requests should be
......@@ -277,7 +283,8 @@ interface IEAS {
/// r: '0xb593...7142',
/// s: '0x0f5b...2cce'
/// },
/// revoker: '0x244934dd3e31bE2c81f84ECf0b3E6329F5381992'
/// revoker: '0x244934dd3e31bE2c81f84ECf0b3E6329F5381992',
/// deadline: 1673891048
/// })
///
/// @param delegatedRequest The arguments of the delegated revocation request.
......@@ -334,7 +341,8 @@ interface IEAS {
/// r: '0x487s...67bb',
/// s: '0x12ad...2366'
/// }],
/// revoker: '0x244934dd3e31bE2c81f84ECf0b3E6329F5381992'
/// revoker: '0x244934dd3e31bE2c81f84ECf0b3E6329F5381992',
/// deadline: 1673891048
/// }])
///
/// @param multiDelegatedRequests The arguments of the delegated multi revocation attestation requests. The requests
......
......@@ -17,7 +17,8 @@ interface ISchemaRegistry {
/// @dev Emitted when a new schema has been registered
/// @param uid The schema UID.
/// @param registerer The address of the account used to register the schema.
event Registered(bytes32 indexed uid, address registerer);
/// @param schema The schema data.
event Registered(bytes32 indexed uid, address indexed registerer, SchemaRecord schema);
/// @dev Submits and reserves a new schema
/// @param schema The schema data schema.
......
......@@ -20,8 +20,8 @@ contract SchemaRegistry is ISchemaRegistry, Semver {
uint256[MAX_GAP - 1] private __gap;
/// @dev Creates a new SchemaRegistry instance.
/// @custom:semver 1.0.3
constructor() Semver(1, 0, 3) { }
/// @custom:semver 1.2.0
constructor() Semver(1, 2, 0) { }
/// @inheritdoc ISchemaRegistry
function register(string calldata schema, ISchemaResolver resolver, bool revocable) external returns (bytes32) {
......@@ -36,7 +36,7 @@ contract SchemaRegistry is ISchemaRegistry, Semver {
schemaRecord.uid = uid;
_registry[uid] = schemaRecord;
emit Registered(uid, msg.sender);
emit Registered(uid, msg.sender, schemaRecord);
return uid;
}
......
......@@ -12,21 +12,31 @@ import {
RevocationRequestData
} from "../IEAS.sol";
import { Signature, InvalidSignature, MAX_GAP, stringToBytes32, bytes32ToString } from "../Common.sol";
import {
DeadlineExpired,
NO_EXPIRATION_TIME,
Signature,
InvalidSignature,
MAX_GAP,
stringToBytes32,
bytes32ToString
} from "../Common.sol";
/// @title EIP1271Verifier
/// @notice EIP1271Verifier typed signatures verifier for EAS delegated attestations.
abstract contract EIP1271Verifier is EIP712 {
using Address for address;
error InvalidNonce();
// The hash of the data type used to relay calls to the attest function. It's the value of
// keccak256("Attest(bytes32 schema,address recipient,uint64 expirationTime,bool revocable,bytes32 refUID,bytes
// data,uint256 nonce)").
bytes32 private constant ATTEST_TYPEHASH = 0xdbfdf8dc2b135c26253e00d5b6cbe6f20457e003fd526d97cea183883570de61;
// data,uint256 value,uint256 nonce,uint64 deadline)").
bytes32 private constant ATTEST_TYPEHASH = 0xf83bb2b0ede93a840239f7e701a54d9bc35f03701f51ae153d601c6947ff3d3f;
// The hash of the data type used to relay calls to the revoke function. It's the value of
// keccak256("Revoke(bytes32 schema,bytes32 uid,uint256 nonce)").
bytes32 private constant REVOKE_TYPEHASH = 0xa98d02348410c9c76735e0d0bb1396f4015ac2bb9615f9c2611d19d7a8a99650;
// keccak256("Revoke(bytes32 schema,bytes32 uid,uint256 value,uint256 nonce,uint64 deadline)").
bytes32 private constant REVOKE_TYPEHASH = 0x2d4116d8c9824e4c316453e5c2843a1885580374159ce8768603c49085ef424c;
// The user readable name of the signing domain.
bytes32 private immutable _name;
......@@ -37,6 +47,11 @@ abstract contract EIP1271Verifier is EIP712 {
// Upgrade forward-compatibility storage gap
uint256[MAX_GAP - 1] private __gap;
/// @dev Emitted when users invalidate nonces by increasing their nonces to (higher) new values.
/// @param oldNonce The previous nonce.
/// @param newNonce The new value.
event NonceIncreased(uint256 oldNonce, uint256 newNonce);
/// @dev Creates a new EIP1271Verifier instance.
/// @param version The current major version of the signing domain
constructor(string memory name, string memory version) EIP712(name, version) {
......@@ -74,17 +89,29 @@ abstract contract EIP1271Verifier is EIP712 {
return bytes32ToString(_name);
}
/// @notice Provides users an option to invalidate nonces by increasing their nonces to (higher) new values.
/// @param newNonce The (higher) new value.
function increaseNonce(uint256 newNonce) external {
uint256 oldNonce = _nonces[msg.sender];
if (newNonce <= oldNonce) {
revert InvalidNonce();
}
_nonces[msg.sender] = newNonce;
emit NonceIncreased({ oldNonce: oldNonce, newNonce: newNonce });
}
/// @notice Verifies delegated attestation request.
/// @param request The arguments of the delegated attestation request.
function _verifyAttest(DelegatedAttestationRequest memory request) internal {
if (request.deadline != NO_EXPIRATION_TIME && request.deadline < _time()) {
revert DeadlineExpired();
}
AttestationRequestData memory data = request.data;
Signature memory signature = request.signature;
uint256 nonce;
unchecked {
nonce = _nonces[request.attester]++;
}
bytes32 hash = _hashTypedDataV4(
keccak256(
abi.encode(
......@@ -95,7 +122,9 @@ abstract contract EIP1271Verifier is EIP712 {
data.revocable,
data.refUID,
keccak256(data.data),
nonce
data.value,
_nonces[request.attester]++,
request.deadline
)
)
);
......@@ -111,15 +140,20 @@ abstract contract EIP1271Verifier is EIP712 {
/// @notice Verifies delegated revocation request.
/// @param request The arguments of the delegated revocation request.
function _verifyRevoke(DelegatedRevocationRequest memory request) internal {
if (request.deadline != NO_EXPIRATION_TIME && request.deadline < _time()) {
revert DeadlineExpired();
}
RevocationRequestData memory data = request.data;
Signature memory signature = request.signature;
uint256 nonce;
unchecked {
nonce = _nonces[request.revoker]++;
}
bytes32 hash = _hashTypedDataV4(keccak256(abi.encode(REVOKE_TYPEHASH, request.schema, data.uid, nonce)));
bytes32 hash = _hashTypedDataV4(
keccak256(
abi.encode(
REVOKE_TYPEHASH, request.schema, data.uid, data.value, _nonces[request.revoker]++, request.deadline
)
)
);
if (
!SignatureChecker.isValidSignatureNow(
request.revoker, hash, abi.encodePacked(signature.r, signature.s, signature.v)
......@@ -128,4 +162,10 @@ abstract contract EIP1271Verifier is EIP712 {
revert InvalidSignature();
}
}
/// @dev Returns the current's block timestamp. This method is overridden during tests and used to simulate the
/// current block time.
function _time() internal view virtual returns (uint64) {
return uint64(block.timestamp);
}
}
......@@ -6,7 +6,7 @@ import { Attestation } from "../Common.sol";
/// @title ISchemaResolver
/// @notice The interface of an optional schema resolver.
interface ISchemaResolver {
/// @notice Checks if the resolve can be sent ETH.
/// @notice Checks if the resolver can be sent ETH.
/// @return Whether the resolver supports ETH transfers.
function isPayable() external pure returns (bool);
......
......@@ -4,14 +4,13 @@ pragma solidity 0.8.19;
import { Semver } from "../../universal/Semver.sol";
import { IEAS, Attestation } from "../IEAS.sol";
import { InvalidEAS, uncheckedInc } from "../Common.sol";
import { AccessDenied, InvalidEAS, InvalidLength, uncheckedInc } from "../Common.sol";
import { ISchemaResolver } from "./ISchemaResolver.sol";
/// @title SchemaResolver
/// @notice The base schema resolver contract.
abstract contract SchemaResolver is ISchemaResolver, Semver {
error AccessDenied();
error InsufficientValue();
error NotPayable();
......@@ -20,7 +19,7 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
/// @dev Creates a new resolver.
/// @param eas The address of the global EAS contract.
constructor(IEAS eas) Semver(1, 0, 1) {
constructor(IEAS eas) Semver(1, 2, 0) {
if (address(eas) == address(0)) {
revert InvalidEAS();
}
......@@ -63,6 +62,9 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
returns (bool)
{
uint256 length = attestations.length;
if (length != values.length) {
revert InvalidLength();
}
// We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting
// from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless
......@@ -77,7 +79,7 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
revert InsufficientValue();
}
// Forward the attestation to the underlying resolver and revert in case it isn't approved.
// Forward the attestation to the underlying resolver and return false in case it isn't approved.
if (!onAttest(attestations[i], value)) {
return false;
}
......@@ -107,6 +109,9 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
returns (bool)
{
uint256 length = attestations.length;
if (length != values.length) {
revert InvalidLength();
}
// We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting
// from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless
......@@ -121,7 +126,7 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
revert InsufficientValue();
}
// Forward the revocation to the underlying resolver and revert in case it isn't approved.
// Forward the revocation to the underlying resolver and return false in case it isn't approved.
if (!onRevoke(attestations[i], value)) {
return false;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment