From 582799ea0589441a2f9a325609af8d37fe8afaf1 Mon Sep 17 00:00:00 2001 From: vimageDE Date: Mon, 16 Dec 2024 16:35:09 +0100 Subject: [PATCH] Attest to Attestation where appropriate --- src/examples/allocator/ServerAllocator.sol | 128 ++++++++++----------- src/interfaces/IServerAllocator.sol | 88 +++++++------- 2 files changed, 108 insertions(+), 108 deletions(-) diff --git a/src/examples/allocator/ServerAllocator.sol b/src/examples/allocator/ServerAllocator.sol index 946177b..da54e3b 100644 --- a/src/examples/allocator/ServerAllocator.sol +++ b/src/examples/allocator/ServerAllocator.sol @@ -15,14 +15,14 @@ import { IERC1271 } from "lib/openzeppelin-contracts/contracts/interfaces/IERC12 contract ServerAllocator is Ownable2Step, EIP712, IServerAllocator { using ECDSA for bytes32; - // keccak256("Attest(address,address,address,uint256,uint256)") + // bytes4(keccak256("attest(address,address,address,uint256,uint256)")). bytes4 private constant _ATTEST_SELECTOR = 0x1a808f91; - // keccak256("RegisterAttest(address signer,bytes32 attestHash,uint256 expiration,uint256 nonce)") - bytes32 private constant _ATTEST_TYPE_HASH = 0xaf2dfd3fe08723f490d203be627da2725f4ad38681e455221da2fc1a633bbb18; + // keccak256("RegisterAttestation(address signer,bytes32 attestationHash,uint256 expiration,uint256 nonce)") + bytes32 private constant _ATTESTATION_TYPE_HASH = 0x6017ed71e505719876ff40d1e87ed2a0a078883c87bd2902ea9988c117f7ca7f; - // keccak256("NonceConsumption(address signer,uint256[] nonces,bytes32[] attests)") - bytes32 private constant _NONCE_CONSUMPTION_TYPE_HASH = 0xb06793f900067653959d9bc53299ebf6b5aa5cf5f6c1a463305891a3db695f3c; + // keccak256("NonceConsumption(address signer,uint256[] nonces,bytes32[] attestations)") + bytes32 private constant _NONCE_CONSUMPTION_TYPE_HASH = 0x626e2c6c331510cafaa5cc323e6ac1e87f32c48cba2a61d81c86b50534f7cc91; address private immutable _COMPACT_CONTRACT; @@ -30,9 +30,9 @@ contract ServerAllocator is Ownable2Step, EIP712, IServerAllocator { mapping(address signer => uint256 index) private _signers; address[] private _activeSigners; - mapping(bytes32 => uint256) private _attestExpirations; - mapping(bytes32 => uint256) private _attestCounts; - mapping(bytes32 => bool) private _attestSignatures; + mapping(bytes32 => uint256) private _attestationExpirations; + mapping(bytes32 => uint256) private _attestationCounts; + mapping(bytes32 => bool) private _attestationSignatures; modifier isSigner(address signer_) { if (!_containsSigner(signer_)) { @@ -74,24 +74,24 @@ contract ServerAllocator is Ownable2Step, EIP712, IServerAllocator { } /// @inheritdoc IServerAllocator - function registerAttest(bytes32 attest_, uint256 expiration_) external isSigner(msg.sender) { - _registerAttest(attest_, expiration_); + function registerAttestation(bytes32 attestation_, uint256 expiration_) external isSigner(msg.sender) { + _registerAttestation(attestation_, expiration_); } /// @inheritdoc IServerAllocator - function registerAttestViaSignature(RegisterAttest calldata attest_, bytes calldata signature_) external { - bytes32 _attestWithNonce = keccak256(abi.encode(attest_.attestHash, attest_.expiration, attest_.nonce)); - if (_attestSignatures[_attestWithNonce]) { - revert AlreadyUsedSig(attest_.attestHash, attest_.nonce); + function registerAttestationViaSignature(RegisterAttestation calldata attestation_, bytes calldata signature_) external { + bytes32 _attestationWithNonce = keccak256(abi.encode(attestation_.attestationHash, attestation_.expiration, attestation_.nonce)); + if (_attestationSignatures[_attestationWithNonce]) { + revert AlreadyUsedSig(attestation_.attestationHash, attestation_.nonce); } - address signer = _validateSignedAttest(attest_.signer, attest_.attestHash, attest_.expiration, attest_.nonce, signature_); - if (signer != attest_.signer || !_containsSigner(signer)) { + address signer = _validateSignedAttestation(attestation_.signer, attestation_.attestationHash, attestation_.expiration, attestation_.nonce, signature_); + if (signer != attestation_.signer || !_containsSigner(signer)) { revert InvalidSignature(signature_, signer); } // Invalidate signature - _attestSignatures[_attestWithNonce] = true; - _registerAttest(attest_.attestHash, attest_.expiration); + _attestationSignatures[_attestationWithNonce] = true; + _registerAttestation(attestation_.attestationHash, attestation_.expiration); } /// @inheritdoc IAllocator @@ -105,46 +105,46 @@ contract ServerAllocator is Ownable2Step, EIP712, IServerAllocator { if (msg.sender != _COMPACT_CONTRACT) { revert InvalidCaller(msg.sender, _COMPACT_CONTRACT); } - bytes32 registeredAttest = keccak256(abi.encode(from_, id_, amount_)); - uint256 count = _attestCounts[registeredAttest]; + bytes32 registeredAttestation = keccak256(abi.encode(from_, id_, amount_)); + uint256 count = _attestationCounts[registeredAttestation]; if (count == 0) { - revert UnregisteredAttest(registeredAttest); + revert UnregisteredAttestation(registeredAttestation); } for (uint256 i = count; i > 0; --i) { - bytes32 countedAttest = keccak256(abi.encode(registeredAttest, i)); - if (_attestExpirations[countedAttest] >= block.timestamp) { - // Found a valid registered attest + bytes32 countedAttestation = keccak256(abi.encode(registeredAttestation, i)); + if (_attestationExpirations[countedAttestation] >= block.timestamp) { + // Found a valid registered attestation if (i == count) { - // Last attest, delete - delete _attestExpirations[countedAttest]; + // Last attestation, delete + delete _attestationExpirations[countedAttestation]; } else { - // Shift attest and delete from the end - bytes32 lastAttest = keccak256(abi.encode(registeredAttest, count)); - _attestExpirations[countedAttest] = _attestExpirations[lastAttest]; - delete _attestExpirations[lastAttest]; + // Shift attestation and delete from the end + bytes32 lastAttestation = keccak256(abi.encode(registeredAttestation, count)); + _attestationExpirations[countedAttestation] = _attestationExpirations[lastAttestation]; + delete _attestationExpirations[lastAttestation]; } - _attestCounts[registeredAttest] = --count; + _attestationCounts[registeredAttestation] = --count; - emit Attested(from_, id_, amount_); + emit AttestationConsumed(from_, id_, amount_); return _ATTEST_SELECTOR; } } - revert ExpiredAttests(registeredAttest); + revert ExpiredAttestations(registeredAttestation); } /// @inheritdoc IServerAllocator - function consume(uint256[] calldata nonces_, bytes32[] calldata attests_) external isSigner(msg.sender) { - if (attests_.length != nonces_.length) { + function consume(uint256[] calldata nonces_, bytes32[] calldata attestations_) external isSigner(msg.sender) { + if (attestations_.length != nonces_.length) { revert InvalidInput(); } - _consumeNonces(nonces_, attests_); + _consumeNonces(nonces_, attestations_); } /// @inheritdoc IServerAllocator function consumeViaSignature(NonceConsumption calldata data_, bytes calldata signature_) external { - if (data_.attests.length != data_.nonces.length) { + if (data_.attestations.length != data_.nonces.length) { revert InvalidInput(); } address signer = _validateNonceConsumption(data_, signature_); @@ -152,7 +152,7 @@ contract ServerAllocator is Ownable2Step, EIP712, IServerAllocator { // first check is optional, can be deleted for gas efficiency revert InvalidSignature(signature_, signer); } - _consumeNonces(data_.nonces, data_.attests); + _consumeNonces(data_.nonces, data_.attestations); } /// @inheritdoc IERC1271 @@ -175,13 +175,13 @@ contract ServerAllocator is Ownable2Step, EIP712, IServerAllocator { } /// @inheritdoc IServerAllocator - function checkAttestExpirations(bytes32 attest_) external view returns (uint256[] memory) { - return _checkAttestExpirations(attest_); + function checkAttestationExpirations(bytes32 attestation_) external view returns (uint256[] memory) { + return _checkAttestationExpirations(attestation_); } /// @inheritdoc IServerAllocator - function checkAttestExpirations(address sponsor_, uint256 id_, uint256 amount_) external view returns (uint256[] memory) { - return _checkAttestExpirations(keccak256(abi.encode(sponsor_, id_, amount_))); + function checkAttestationExpirations(address sponsor_, uint256 id_, uint256 amount_) external view returns (uint256[] memory) { + return _checkAttestationExpirations(keccak256(abi.encode(sponsor_, id_, amount_))); } /// @inheritdoc IServerAllocator @@ -189,45 +189,45 @@ contract ServerAllocator is Ownable2Step, EIP712, IServerAllocator { return _COMPACT_CONTRACT; } - function _registerAttest(bytes32 attest_, uint256 expiration_) internal { + function _registerAttestation(bytes32 attestation_, uint256 expiration_) internal { if (expiration_ < block.timestamp) { revert Expired(expiration_, block.timestamp); } - uint256 count = ++_attestCounts[attest_]; - bytes32 countedAttest = keccak256(abi.encode(attest_, count)); + uint256 count = ++_attestationCounts[attestation_]; + bytes32 countedAttestation = keccak256(abi.encode(attestation_, count)); - _attestExpirations[countedAttest] = expiration_; + _attestationExpirations[countedAttestation] = expiration_; - emit AttestRegistered(attest_, expiration_); + emit AttestationRegistered(attestation_, expiration_); } /// Todo: This will lead to always the last registered hash being consumed. - function _consumeNonces(uint256[] calldata nonces_, bytes32[] calldata attests_) internal { + function _consumeNonces(uint256[] calldata nonces_, bytes32[] calldata attestations_) internal { ITheCompact(_COMPACT_CONTRACT).consume(nonces_); - uint256 nonceLength = attests_.length; + uint256 nonceLength = attestations_.length; for (uint256 i = 0; i < nonceLength; ++i) { - bytes32 hashToConsume = attests_[i]; + bytes32 hashToConsume = attestations_[i]; if (hashToConsume != bytes32(0)) { - uint256 count = _attestCounts[attests_[i]]; + uint256 count = _attestationCounts[attestations_[i]]; if (count != 0) { - // Consume the latest registered attest - delete _attestExpirations[ - keccak256(abi.encode(attests_[i], count)) + // Consume the latest registered attestation + delete _attestationExpirations[ + keccak256(abi.encode(attestations_[i], count)) ]; - _attestCounts[attests_[i]] = --count; + _attestationCounts[attestations_[i]] = --count; } } } emit NoncesConsumed(nonces_); } - function _validateSignedAttest(address signer_, bytes32 hash_, uint256 expiration_, uint256 nonce, bytes calldata signature_) internal view returns (address) { - bytes32 message = _hashAttest(signer_, hash_, expiration_, nonce); + function _validateSignedAttestation(address signer_, bytes32 hash_, uint256 expiration_, uint256 nonce, bytes calldata signature_) internal view returns (address) { + bytes32 message = _hashAttestation(signer_, hash_, expiration_, nonce); return message.recover(signature_); } - function _hashAttest(address signer_, bytes32 hash_, uint256 expiration_, uint256 nonce_) internal view returns (bytes32) { - return _hashTypedDataV4(keccak256(abi.encode(_ATTEST_TYPE_HASH, signer_, hash_, expiration_, nonce_))); + function _hashAttestation(address signer_, bytes32 hash_, uint256 expiration_, uint256 nonce_) internal view returns (bytes32) { + return _hashTypedDataV4(keccak256(abi.encode(_ATTESTATION_TYPE_HASH, signer_, hash_, expiration_, nonce_))); } function _validateSignedHash(bytes32 digest_, bytes calldata signature_) internal pure returns (address) { @@ -240,21 +240,21 @@ contract ServerAllocator is Ownable2Step, EIP712, IServerAllocator { } function _hashNonceConsumption(NonceConsumption calldata data_) internal view returns (bytes32) { - return _hashTypedDataV4(keccak256(abi.encode(_NONCE_CONSUMPTION_TYPE_HASH, data_.signer, data_.nonces, data_.attests))); + return _hashTypedDataV4(keccak256(abi.encode(_NONCE_CONSUMPTION_TYPE_HASH, data_.signer, data_.nonces, data_.attestations))); } function _containsSigner(address signer_) internal view returns (bool) { return _signers[signer_] != 0; } - function _checkAttestExpirations(bytes32 attest_) internal view returns (uint256[] memory) { - uint256 count = _attestCounts[attest_]; + function _checkAttestationExpirations(bytes32 attestation_) internal view returns (uint256[] memory) { + uint256 count = _attestationCounts[attestation_]; if (count == 0) { - revert UnregisteredAttest(attest_); + revert UnregisteredAttestation(attestation_); } uint256[] memory expirations = new uint256[](count); for (uint256 i = count; i > 0; --i) { - expirations[i - 1] = _attestExpirations[keccak256(abi.encode(attest_, i))]; + expirations[i - 1] = _attestationExpirations[keccak256(abi.encode(attestation_, i))]; } return expirations; } diff --git a/src/interfaces/IServerAllocator.sol b/src/interfaces/IServerAllocator.sol index c36f405..75802de 100644 --- a/src/interfaces/IServerAllocator.sol +++ b/src/interfaces/IServerAllocator.sol @@ -5,34 +5,34 @@ pragma solidity ^0.8.27; import { IAllocator } from "src/interfaces/IAllocator.sol"; interface IServerAllocator is IAllocator { - struct RegisterAttest { - // The address of the signer who must sign the attest + struct RegisterAttestation { + // The address of the signer who must sign the attestation address signer; - // The hash of the attest information, consistent of sponsor, id and amount - bytes32 attestHash; - // The expiration date after which the attest is no longer valid + // The hash of the attestation information, consistent of sponsor, id and amount + bytes32 attestationHash; + // The expiration date after which the attestation is no longer valid uint256 expiration; - // A nonce for that specific attest hash to prevent replay attacks + // A nonce for that specific attestation hash to prevent replay attacks uint256 nonce; } struct NonceConsumption { - // The address of the signer who must sign the attests + // The address of the signer who must sign the attestations address signer; // The array of nonces that should be consumed uint256[] nonces; - // The array of previously registered attests that should be consumed - bytes32[] attests; + // The array of previously registered attestations that should be consumed + bytes32[] attestations; } - /// @notice Thrown if no attest was registered for the given transfer - error UnregisteredAttest(bytes32 attest_); + /// @notice Thrown if no attestation was registered for the given transfer + error UnregisteredAttestation(bytes32 attestation_); - /// @notice Thrown if the expiration date to register an attest is in the past + /// @notice Thrown if the expiration date to register an attestation is in the past error Expired(uint256 expiration_, uint256 currentTimestamp_); - /// @notice Thrown if all of the registered attests have expired - error ExpiredAttests(bytes32 attest_); + /// @notice Thrown if all of the registered attestations have expired + error ExpiredAttestations(bytes32 attestation_); /// @notice Thrown if the caller of attest is not the compact contract error InvalidCaller(address caller_, address expected_); @@ -44,7 +44,7 @@ interface IServerAllocator is IAllocator { error InvalidSignature(bytes signature_, address signer_); /// @notice Thrown if the same signature is used multiple times - error AlreadyUsedSig(bytes32 attest_, uint256 nonce); + error AlreadyUsedSig(bytes32 attestation_, uint256 nonce); /// @notice Thrown if the input array lengths are not matching error InvalidInput(); @@ -57,20 +57,20 @@ interface IServerAllocator is IAllocator { /// @param signer_ The address of the signer event SignerRemoved(address signer_); - /// @notice Emitted when an attest is registered - /// @param attest_ The hash of the attest, consistent of sponsor, id and amount - /// @param expiration_ The expiration date of the attest - event AttestRegistered(bytes32 attest_, uint256 expiration_); + /// @notice Emitted when an attestation is registered + /// @param attestation_ The hash of the attestation, consistent of sponsor, id and amount + /// @param expiration_ The expiration date of the attestation + event AttestationRegistered(bytes32 attestation_, uint256 expiration_); /// @notice Emitted when nonces on the compact contract are consumed successfully /// @param nonces_ The array of nonces that were consumed event NoncesConsumed(uint256[] nonces_); - /// @notice Emitted when an attest was consumed for a transfer + /// @notice Emitted when an attestation was consumed for a transfer /// @param from_ The address of the sponsor /// @param id_ The id of the token that was transferred /// @param amount_ The amount of the token that was transferred - event Attested(address from_, uint256 id_, uint256 amount_); + event AttestationConsumed(address from_, uint256 id_, uint256 amount_); /// @notice Add a signer to the allocator /// @dev Only the owner can add a signer @@ -82,28 +82,28 @@ interface IServerAllocator is IAllocator { /// @param signer_ The address of the signer to remove function removeSigner(address signer_) external; - /// @notice Register an attest for a transfer - /// @dev There is no way to uniquely identify a transfer, so the contract relies on its own accounting of registered attests. - /// @param attest_ The hash of the attest to whitelist, consistent of sponsor, id and amount - /// @param expiration_ The expiration date of the attest - function registerAttest(bytes32 attest_, uint256 expiration_) external; + /// @notice Register an attestation for a transfer + /// @dev There is no way to uniquely identify a transfer, so the contract relies on its own accounting of registered attestations. + /// @param attestation_ The hash of the attestation to whitelist, consistent of sponsor, id and amount + /// @param expiration_ The expiration date of the attestation + function registerAttestation(bytes32 attestation_, uint256 expiration_) external; - /// @notice Register an attest for a transfer via a signature - /// @dev Nonce management in the RegisterAttest is only required for multiple registers of the same attest with the same expiration. - /// @param attest_ The RegisterAttest struct containing the signer, the hash of the attest, the expiration and the nonce + /// @notice Register an attestation for a transfer via a signature + /// @dev Nonce management in the RegisterAttestation is only required for multiple registers of the same attestation with the same expiration. + /// @param attestation_ The RegisterAttestation struct containing the signer, the hash of the attestation, the expiration and the nonce /// @param signature_ The signature of the signer - function registerAttestViaSignature(RegisterAttest calldata attest_, bytes calldata signature_) external; + function registerAttestationViaSignature(RegisterAttestation calldata attestation_, bytes calldata signature_) external; - /// @notice Consume nonces on the compact contract and attests on the allocator + /// @notice Consume nonces on the compact contract and attestations on the allocator /// @dev The hashes array needs to be of the same length as the nonces array. /// @dev If no hash was yet registered for the respective nonce, provide a bytes32(0) for the index. /// @dev All signers can override nonces of other signers. /// @param nonces_ The array of all nonces to consume on the compact contract - /// @param attests_ The array of all attests to consume on the allocator - function consume(uint256[] calldata nonces_, bytes32[] calldata attests_) external; + /// @param attestations_ The array of all attestations to consume on the allocator + function consume(uint256[] calldata nonces_, bytes32[] calldata attestations_) external; - /// @notice Consume nonces on the compact contract and attests on the allocator via a signature - /// @param data_ The NonceConsumption struct containing the signer, the array of nonces and the array of attests + /// @notice Consume nonces on the compact contract and attestations on the allocator via a signature + /// @param data_ The NonceConsumption struct containing the signer, the array of nonces and the array of attestations /// @param signature_ The signature of the signer function consumeViaSignature(NonceConsumption calldata data_, bytes calldata signature_) external; @@ -116,19 +116,19 @@ interface IServerAllocator is IAllocator { /// @return The array of all registered signers function getAllSigners() external view returns (address[] memory); - /// @notice Check the expiration dates of an attest - /// @dev If no attest was registered for the provided hash, the function will revert - /// @param attest_ The hash of the attest to check - /// @return The array of expiration dates for the registered attests - function checkAttestExpirations(bytes32 attest_) external view returns (uint256[] memory); + /// @notice Check the expiration dates of an attestation + /// @dev If no attestation was registered for the provided hash, the function will revert + /// @param attestation_ The hash of the attestation to check + /// @return The array of expiration dates for the registered attestations + function checkAttestationExpirations(bytes32 attestation_) external view returns (uint256[] memory); - /// @notice Check the expiration dates of an attest by its components - /// @dev If no attest was registered for the provided components, the function will revert + /// @notice Check the expiration dates of an attestation by its components + /// @dev If no attestation was registered for the provided components, the function will revert /// @param sponsor_ The address of the sponsor /// @param id_ The id of the token /// @param amount_ The amount of the token - /// @return The array of expiration dates for the registered attests - function checkAttestExpirations(address sponsor_, uint256 id_, uint256 amount_) external view returns (uint256[] memory); + /// @return The array of expiration dates for the registered attestations + function checkAttestationExpirations(address sponsor_, uint256 id_, uint256 amount_) external view returns (uint256[] memory); /// @notice Get the address of the compact contract /// @dev Only the compact contract can call the attest function