-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(excubiae): add basic EASExcubia extension
- Loading branch information
Showing
10 changed files
with
405 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
module.exports = { | ||
istanbulFolder: "../../coverage/gatekeepers" | ||
istanbulFolder: "../../coverage/excubiae", | ||
skipFiles: ["test"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0 <0.9.0; | ||
|
||
import {Excubia} from "../Excubia.sol"; | ||
import {IEAS} from "@ethereum-attestation-service/eas-contracts/contracts/IEAS.sol"; | ||
import {Attestation} from "@ethereum-attestation-service/eas-contracts/contracts/Common.sol"; | ||
|
||
/// @title EAS Excubia Contract. | ||
/// @notice This contract extends the Excubia contract to integrate with the Ethereum Attestation Service (EAS). | ||
/// This contract checks an EAS attestation to permit access through the gate. | ||
/// @dev The contract uses a specific attestation schema & attester to admit the recipient of the attestation. | ||
contract EASExcubia is Excubia { | ||
/// @notice The Ethereum Attestation Service contract interface. | ||
IEAS public immutable EAS; | ||
/// @notice The specific schema ID that attestations must match to pass the gate. | ||
bytes32 public immutable SCHEMA; | ||
/// @notice The trusted attester address whose attestations are considered | ||
/// the only ones valid to pass the gate. | ||
address public immutable ATTESTER; | ||
|
||
/// @notice Mapping to track which attestations have been registered by the contract to | ||
/// avoid double checks with the same attestation. | ||
mapping(bytes32 => bool) public registeredAttestations; | ||
|
||
/// @notice Error thrown when the attestation has been already used to pass the gate. | ||
error AlreadyRegistered(); | ||
|
||
/// @notice Error thrown when the attestation does not match the designed schema. | ||
error UnexpectedSchema(); | ||
|
||
/// @notice Error thrown when the attestation does not match the designed trusted attester. | ||
error UnexpectedAttester(); | ||
|
||
/// @notice Error thrown when the attestation does not match the passerby as recipient. | ||
error UnexpectedRecipient(); | ||
|
||
/// @notice Error thrown when the attestation has been revoked. | ||
error RevokedAttestation(); | ||
|
||
/// @notice Constructor to initialize with target EAS contract with specific attester and schema. | ||
/// @param _eas The address of the EAS contract. | ||
/// @param _attester The address of the trusted attester. | ||
/// @param _schema The schema ID that attestations must match. | ||
constructor(address _eas, address _attester, bytes32 _schema) { | ||
if (_eas == address(0) || _attester == address(0)) revert ZeroAddress(); | ||
|
||
EAS = IEAS(_eas); | ||
ATTESTER = _attester; | ||
SCHEMA = _schema; | ||
} | ||
|
||
/// @notice Overrides the `_pass` function to register a correct attestation. | ||
/// @param passerby The address of the entity attempting to pass the gate. | ||
/// @param data Encoded attestation ID. | ||
function _pass(address passerby, bytes calldata data) internal override { | ||
super._pass(passerby, data); | ||
|
||
registeredAttestations[decodeAttestationId(data)] = true; | ||
} | ||
|
||
/// @notice Overrides the `_check` function to validate the attestation against specific criteria. | ||
/// @param passerby The address of the entity attempting to pass the gate. | ||
/// @param data Encoded attestation ID. | ||
/// @return True if the attestation meets all criteria, revert otherwise. | ||
function _check(address passerby, bytes calldata data) internal view override returns (bool) { | ||
bytes32 attestationId = decodeAttestationId(data); | ||
|
||
if (registeredAttestations[attestationId]) revert AlreadyRegistered(); | ||
|
||
Attestation memory attestation = EAS.getAttestation(attestationId); | ||
|
||
if (attestation.schema != SCHEMA) revert UnexpectedSchema(); | ||
if (attestation.attester != ATTESTER) revert UnexpectedAttester(); | ||
if (attestation.recipient != passerby) revert UnexpectedRecipient(); | ||
if (attestation.revocationTime != 0) revert RevokedAttestation(); | ||
|
||
return true; | ||
} | ||
|
||
/// @notice Decodes an EAS attestation identifier from the encoded form. | ||
/// @param data Encoded attestation ID. | ||
/// @return Decoded attestation ID. | ||
function decodeAttestationId(bytes calldata data) private pure returns (bytes32) { | ||
return abi.decode(data, (bytes32)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
/* solhint-disable max-line-length */ | ||
import {IEAS, ISchemaRegistry, AttestationRequest, MultiAttestationRequest, DelegatedAttestationRequest, MultiDelegatedAttestationRequest, DelegatedRevocationRequest, RevocationRequest, MultiRevocationRequest, MultiDelegatedRevocationRequest} from "@ethereum-attestation-service/eas-contracts/contracts/IEAS.sol"; | ||
import {Attestation} from "@ethereum-attestation-service/eas-contracts/contracts/Common.sol"; | ||
|
||
/// @title Mock Ethereum Attestation Service (EAS) Contract. | ||
/// @notice This contract is a mock implementation of the IEAS interface for testing purposes. | ||
/// @dev It simulates the behavior of a real EAS contract by providing predefined mocked attestations. | ||
contract MockEAS is IEAS { | ||
/// @notice A mock schema registry, represented simply as an address. | ||
ISchemaRegistry public override getSchemaRegistry; | ||
|
||
/// @notice A mapping to store mocked attestations by their unique identifiers. | ||
mapping(bytes32 => Attestation) private mockedAttestations; | ||
|
||
/// MOCKS /// | ||
|
||
/// @notice Constructor to initialize the mock contract with predefined attestations. | ||
/// @param _recipient The recipient address used in mocked attestations. | ||
/// @param _attester The attester address used in mocked attestations. | ||
/// @param _schema The schema identifier used in mocked attestations. | ||
constructor(address _recipient, address _attester, bytes32 _schema) { | ||
getSchemaRegistry = ISchemaRegistry(address(1)); | ||
|
||
Attestation memory valid = Attestation({ | ||
uid: bytes32("0x01"), | ||
schema: _schema, | ||
time: 0, | ||
expirationTime: 0, | ||
revocationTime: 0, | ||
refUID: bytes32("0x01"), | ||
recipient: _recipient, | ||
attester: _attester, | ||
revocable: true, | ||
data: bytes("") | ||
}); | ||
|
||
Attestation memory revoked = Attestation({ | ||
uid: bytes32("0x02"), | ||
schema: _schema, | ||
time: 0, | ||
expirationTime: 0, | ||
revocationTime: 1, | ||
refUID: bytes32("0x01"), | ||
recipient: _recipient, | ||
attester: _attester, | ||
revocable: true, | ||
data: bytes("") | ||
}); | ||
|
||
Attestation memory invalidSchema = Attestation({ | ||
uid: bytes32("0x03"), | ||
schema: bytes32("0x01"), | ||
time: 0, | ||
expirationTime: 0, | ||
revocationTime: 0, | ||
refUID: bytes32("0x01"), | ||
recipient: _recipient, | ||
attester: _attester, | ||
revocable: true, | ||
data: bytes("") | ||
}); | ||
|
||
Attestation memory invalidRecipient = Attestation({ | ||
uid: bytes32("0x04"), | ||
schema: _schema, | ||
time: 0, | ||
expirationTime: 0, | ||
revocationTime: 0, | ||
refUID: bytes32("0x01"), | ||
recipient: address(1), | ||
attester: _attester, | ||
revocable: true, | ||
data: bytes("") | ||
}); | ||
|
||
Attestation memory invalidAttester = Attestation({ | ||
uid: bytes32("0x05"), | ||
schema: _schema, | ||
time: 0, | ||
expirationTime: 0, | ||
revocationTime: 0, | ||
refUID: bytes32("0x000000000000000000000000000001"), | ||
recipient: _recipient, | ||
attester: address(1), | ||
revocable: true, | ||
data: bytes("") | ||
}); | ||
|
||
mockedAttestations[bytes32("0x01")] = valid; | ||
mockedAttestations[bytes32("0x02")] = revoked; | ||
mockedAttestations[bytes32("0x03")] = invalidSchema; | ||
mockedAttestations[bytes32("0x04")] = invalidRecipient; | ||
mockedAttestations[bytes32("0x05")] = invalidAttester; | ||
} | ||
|
||
/// @notice Retrieves a mocked attestation by its unique identifier. | ||
/// @param uid The unique identifier of the attestation. | ||
/// @return The mocked attestation associated with the given identifier. | ||
function getAttestation(bytes32 uid) external view override returns (Attestation memory) { | ||
return mockedAttestations[uid]; | ||
} | ||
|
||
/// STUBS /// | ||
// The following functions are stubs and do not perform any meaningful operations. | ||
// They are placeholders to comply with the IEAS interface. | ||
function attest(AttestationRequest calldata /*request*/) external payable override returns (bytes32) { | ||
return bytes32(0); | ||
} | ||
|
||
function attestByDelegation( | ||
DelegatedAttestationRequest calldata /*delegatedRequest*/ | ||
) external payable override returns (bytes32) { | ||
return bytes32(0); | ||
} | ||
|
||
function multiAttest( | ||
MultiAttestationRequest[] calldata multiRequests | ||
) external payable override returns (bytes32[] memory) { | ||
return new bytes32[](multiRequests.length); | ||
} | ||
|
||
function multiAttestByDelegation( | ||
MultiDelegatedAttestationRequest[] calldata multiDelegatedRequests | ||
) external payable override returns (bytes32[] memory) { | ||
return new bytes32[](multiDelegatedRequests.length); | ||
} | ||
|
||
function revoke(RevocationRequest calldata request) external payable override {} | ||
|
||
function revokeByDelegation(DelegatedRevocationRequest calldata delegatedRequest) external payable override {} | ||
|
||
function multiRevoke(MultiRevocationRequest[] calldata multiRequests) external payable override {} | ||
|
||
function multiRevokeByDelegation( | ||
MultiDelegatedRevocationRequest[] calldata multiDelegatedRequests | ||
) external payable override {} | ||
|
||
function timestamp(bytes32 /*data*/) external view override returns (uint64) { | ||
return uint64(block.timestamp); | ||
} | ||
|
||
function multiTimestamp(bytes32[] calldata /*data*/) external view override returns (uint64) { | ||
return uint64(block.timestamp); | ||
} | ||
|
||
function revokeOffchain(bytes32 /*data*/) external view override returns (uint64) { | ||
return uint64(block.timestamp); | ||
} | ||
|
||
function multiRevokeOffchain(bytes32[] calldata /*data*/) external view override returns (uint64) { | ||
return uint64(block.timestamp); | ||
} | ||
|
||
function isAttestationValid(bytes32 uid) external view override returns (bool) { | ||
return mockedAttestations[uid].uid != bytes32(0); | ||
} | ||
|
||
function getTimestamp(bytes32 /*data*/) external view override returns (uint64) { | ||
return uint64(block.timestamp); | ||
} | ||
|
||
function getRevokeOffchain(address /*revoker*/, bytes32 /*data*/) external view override returns (uint64) { | ||
return uint64(block.timestamp); | ||
} | ||
|
||
function version() external pure returns (string memory) { | ||
return string(""); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Oops, something went wrong.