diff --git a/src/AttestationRegistry.sol b/src/AttestationRegistry.sol index 3c3772c9..a60c9f62 100644 --- a/src/AttestationRegistry.sol +++ b/src/AttestationRegistry.sol @@ -34,17 +34,19 @@ contract AttestationRegistry is OwnableUpgradeable { error AttestationSubjectFieldEmpty(); /// @notice Error thrown when an attestation data field is empty error AttestationDataFieldEmpty(); + /// @notice Error thrown when an attempt is made to bulk replace with mismatched parameter array lengths + error ArrayLengthMismatch(); /// @notice Error thrown when an attempt is made to revoke an attestation that was already revoked error AlreadyRevoked(); /// @notice Error thrown when an attempt is made to revoke an attestation based on a non-revocable schema error AttestationNotRevocable(); - /// @notice Error thrown when attempting to bulk revoke attestations without the same number of replacing attestations - error BulkRevocationInvalidParams(); /// @notice Event emitted when an attestation is registered event AttestationRegistered(bytes32 indexed attestationId); + /// @notice Event emitted when an attestation is replaced + event AttestationReplaced(bytes32 attestationId, bytes32 replacedBy); /// @notice Event emitted when an attestation is revoked - event AttestationRevoked(bytes32 attestationId, bytes32 replacedBy); + event AttestationRevoked(bytes32 attestationId); /// @notice Event emitted when the version number is incremented event VersionUpdated(uint16 version); @@ -147,11 +149,42 @@ contract AttestationRegistry is OwnableUpgradeable { } /** - * @notice Revokes an attestation for given identifier and can replace it by an other one - * @param attestationId the attestation ID to revoke - * @param replacedBy the replacing attestation ID (leave empty to just revoke) + * @notice Replaces an attestation for the given identifier and replaces it with a new attestation + * @param attestationId the ID of the attestation to replace + * @param attestationPayload the attestation payload to create the new attestation and register it + * @param attester the account address issuing the attestation + */ + function replace(bytes32 attestationId, AttestationPayload calldata attestationPayload, address attester) public { + attest(attestationPayload, attester); + revoke(attestationId); + bytes32 replacedBy = bytes32(abi.encode(attestationIdCounter)); + attestations[attestationId].replacedBy = replacedBy; + + emit AttestationReplaced(attestationId, replacedBy); + } + + /** + * @notice Replaces attestations for given identifiers and replaces them with new attestations + * @param attestationIds the list of IDs of the attestations to replace + * @param attestationPayloads the list of attestation payloads to create the new attestations and register them + * @param attester the account address issuing the attestation */ - function revoke(bytes32 attestationId, bytes32 replacedBy) public { + function bulkReplace( + bytes32[] calldata attestationIds, + AttestationPayload[] calldata attestationPayloads, + address attester + ) public { + if (attestationIds.length != attestationPayloads.length) revert ArrayLengthMismatch(); + for (uint256 i = 0; i < attestationIds.length; i++) { + replace(attestationIds[i], attestationPayloads[i], attester); + } + } + + /** + * @notice Revokes an attestation for a given identifier + * @param attestationId the ID of the attestation to revoke + */ + function revoke(bytes32 attestationId) public { if (!isRegistered(attestationId)) revert AttestationNotAttested(); if (attestations[attestationId].revoked) revert AlreadyRevoked(); if (msg.sender != attestations[attestationId].portal) revert OnlyAttestingPortal(); @@ -160,21 +193,16 @@ contract AttestationRegistry is OwnableUpgradeable { attestations[attestationId].revoked = true; attestations[attestationId].revocationDate = uint64(block.timestamp); - if (isRegistered(replacedBy)) attestations[attestationId].replacedBy = replacedBy; - - emit AttestationRevoked(attestationId, replacedBy); + emit AttestationRevoked(attestationId); } /** - * @notice Bulk revokes attestations for given identifiers and can replace them by new ones - * @param attestationIds the attestations IDs to revoke - * @param replacedBy the replacing attestations IDs (leave an ID empty to just revoke) + * @notice Bulk revokes a list of attestations for the given identifiers + * @param attestationIds the IDs of the attestations to revoke */ - function bulkRevoke(bytes32[] memory attestationIds, bytes32[] memory replacedBy) external { - if (attestationIds.length != replacedBy.length) revert BulkRevocationInvalidParams(); - + function bulkRevoke(bytes32[] memory attestationIds) external { for (uint256 i = 0; i < attestationIds.length; i++) { - revoke(attestationIds[i], replacedBy[i]); + revoke(attestationIds[i]); } } diff --git a/src/example/EASPortal.sol b/src/example/EASPortal.sol index ca2daa4d..a3b22af4 100644 --- a/src/example/EASPortal.sol +++ b/src/example/EASPortal.sol @@ -74,11 +74,11 @@ contract EASPortal is AbstractPortal { } } - function revoke(bytes32 /*attestationId*/, bytes32 /*replacedBy*/) public pure override { + function _onRevoke(bytes32 /*attestationId*/) internal pure override { revert NoRevocation(); } - function bulkRevoke(bytes32[] memory /*attestationIds*/, bytes32[] memory /*replacedBy*/) public pure override { + function _onBulkRevoke(bytes32[] memory /*attestationIds*/) internal pure override { revert NoBulkRevocation(); } diff --git a/src/interface/AbstractPortal.sol b/src/interface/AbstractPortal.sol index 1befdafe..11637aa0 100644 --- a/src/interface/AbstractPortal.sol +++ b/src/interface/AbstractPortal.sol @@ -47,10 +47,7 @@ abstract contract AbstractPortal is Initializable, IERC165Upgradeable { * @param validationPayloads the payloads to validate via the modules to issue the attestations * @dev Runs all modules for the portal and registers the attestation using AttestationRegistry */ - function attest( - AttestationPayload memory attestationPayload, - bytes[] memory validationPayloads - ) public payable virtual { + function attest(AttestationPayload memory attestationPayload, bytes[] memory validationPayloads) public payable { moduleRegistry.runModules(modules, attestationPayload, validationPayloads, msg.value); _onAttest(attestationPayload); @@ -66,40 +63,71 @@ abstract contract AbstractPortal is Initializable, IERC165Upgradeable { function bulkAttest( AttestationPayload[] memory attestationsPayloads, bytes[][] memory validationPayloads - ) public payable virtual { - // Run all modules for all payloads + ) public payable { moduleRegistry.bulkRunModules(modules, attestationsPayloads, validationPayloads); _onBulkAttest(attestationsPayloads, validationPayloads); - // Register attestations using the attestation registry attestationRegistry.bulkAttest(attestationsPayloads, _getAttester()); } /** - * @notice Revokes the attestation for the given identifier and can replace it by a new one - * @param attestationId the attestation ID to revoke - * @param replacedBy the replacing attestation ID (leave empty to just revoke) + * @notice Replaces the attestation for the given identifier and replaces it with a new attestation + * @param attestationId the ID of the attestation to replace + * @param attestationPayload the attestation payload to create the new attestation and register it + * @param validationPayloads the payloads to validate via the modules to issue the attestation + * @dev Runs all modules for the portal and registers the attestation using AttestationRegistry + */ + function replace( + bytes32 attestationId, + AttestationPayload memory attestationPayload, + bytes[] memory validationPayloads + ) public payable { + moduleRegistry.runModules(modules, attestationPayload, validationPayloads, msg.value); + + _onReplace(attestationId, attestationPayload); + + attestationRegistry.replace(attestationId, attestationPayload, _getAttester()); + } + + /** + * @notice Bulk replaces the attestation for the given identifiers and replaces them with new attestations + * @param attestationIds the list of IDs of the attestations to replace + * @param attestationsPayloads the list of attestation payloads to create the new attestations and register them + * @param validationPayloads the payloads to validate via the modules to issue the attestations + */ + function bulkReplace( + bytes32[] memory attestationIds, + AttestationPayload[] memory attestationsPayloads, + bytes[][] memory validationPayloads + ) public payable { + moduleRegistry.bulkRunModules(modules, attestationsPayloads, validationPayloads); + + _onBulkReplace(attestationIds, attestationsPayloads, validationPayloads); + + attestationRegistry.bulkReplace(attestationIds, attestationsPayloads, _getAttester()); + } + + /** + * @notice Revokes an attestation for the given identifier + * @param attestationId the ID of the attestation to revoke * @dev By default, revocation is only possible by the portal owner * We strongly encourage implementing such a rule in your Portal if you intend on overriding this method */ - function revoke(bytes32 attestationId, bytes32 replacedBy) public virtual { - if (msg.sender != portalRegistry.getPortalByAddress(address(this)).ownerAddress) revert OnlyPortalOwner(); + function revoke(bytes32 attestationId) public { + _onRevoke(attestationId); - _onRevoke(attestationId, replacedBy); - - attestationRegistry.revoke(attestationId, replacedBy); + attestationRegistry.revoke(attestationId); } /** - * @notice Bulk revokes attestations for given identifiers and can replace them by new ones - * @param attestationIds the attestations IDs to revoke - * @param replacedBy the replacing attestations IDs (leave an ID empty to just revoke) + * @notice Bulk revokes a list of attestations for the given identifiers + * @param attestationIds the IDs of the attestations to revoke */ - function bulkRevoke(bytes32[] memory attestationIds, bytes32[] memory replacedBy) public virtual { - _onBulkRevoke(attestationIds, replacedBy); + function bulkRevoke(bytes32[] memory attestationIds) public { + _onBulkRevoke(attestationIds); - attestationRegistry.bulkRevoke(attestationIds, replacedBy); + attestationRegistry.bulkRevoke(attestationIds); } /** @@ -133,6 +161,13 @@ abstract contract AbstractPortal is Initializable, IERC165Upgradeable { */ function _onAttest(AttestationPayload memory attestationPayload) internal virtual {} + /** + * @notice Optional method run when an attestation is replaced + * @param attestationId the ID of the attestation being replaced + * @param attestationPayload the attestation payload to create attestation and register it + */ + function _onReplace(bytes32 attestationId, AttestationPayload memory attestationPayload) internal virtual {} + /** * @notice Optional method run when attesting a batch of payloads * @param attestationsPayloads the payloads to attest @@ -143,17 +178,25 @@ abstract contract AbstractPortal is Initializable, IERC165Upgradeable { bytes[][] memory validationPayloads ) internal virtual {} + function _onBulkReplace( + bytes32[] memory attestationIds, + AttestationPayload[] memory attestationsPayloads, + bytes[][] memory validationPayloads + ) internal virtual {} + /** * @notice Optional method run when an attestation is revoked or replaced - * @param attestationId the attestation ID to revoke - * @param replacedBy the replacing attestation ID + * @dev IMPORTANT NOTE: By default, revocation is only possible by the portal owner */ - function _onRevoke(bytes32 attestationId, bytes32 replacedBy) internal virtual {} + function _onRevoke(bytes32 /*attestationId*/) internal virtual { + if (msg.sender != portalRegistry.getPortalByAddress(address(this)).ownerAddress) revert OnlyPortalOwner(); + } /** * @notice Optional method run when a batch of attestations are revoked or replaced - * @param attestationIds the attestations IDs to revoke - * @param replacedBy the replacing attestations IDs + * @dev IMPORTANT NOTE: By default, revocation is only possible by the portal owner */ - function _onBulkRevoke(bytes32[] memory attestationIds, bytes32[] memory replacedBy) internal virtual {} + function _onBulkRevoke(bytes32[] memory /*attestationIds*/) internal virtual { + if (msg.sender != portalRegistry.getPortalByAddress(address(this)).ownerAddress) revert OnlyPortalOwner(); + } } diff --git a/src/portal/DefaultPortal.sol b/src/portal/DefaultPortal.sol index 93a76ae6..067ac041 100644 --- a/src/portal/DefaultPortal.sol +++ b/src/portal/DefaultPortal.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.21; import { AbstractPortal } from "../interface/AbstractPortal.sol"; -import { AttestationPayload } from "../types/Structs.sol"; /** * @title Default Portal diff --git a/test/AttestationRegistry.t.sol b/test/AttestationRegistry.t.sol index e8d5a67c..342539f2 100644 --- a/test/AttestationRegistry.t.sol +++ b/test/AttestationRegistry.t.sol @@ -21,8 +21,9 @@ contract AttestationRegistryTest is Test { event Initialized(uint8 version); event AttestationRegistered(bytes32 indexed attestationId); event BulkAttestationsRegistered(Attestation[] attestations); - event AttestationRevoked(bytes32 attestationId, bytes32 replacedBy); - event BulkAttestationsRevoked(bytes32[] attestationId, bytes32[] replacedBy); + event AttestationReplaced(bytes32 attestationId, bytes32 replacedBy); + event AttestationRevoked(bytes32 attestationId); + event BulkAttestationsRevoked(bytes32[] attestationId); event VersionUpdated(uint16 version); function setUp() public { @@ -177,6 +178,87 @@ contract AttestationRegistryTest is Test { assertTrue(isRegistered2); } + function test_replace(AttestationPayload memory attestationPayload) public { + vm.assume(attestationPayload.subject.length != 0); + vm.assume(attestationPayload.attestationData.length != 0); + + SchemaRegistryMock schemaRegistryMock = SchemaRegistryMock(router.getSchemaRegistry()); + attestationPayload.schemaId = schemaRegistryMock.getIdFromSchemaString("schemaString"); + + Attestation memory attestation = _createAttestation(attestationPayload, 1); + vm.prank(portal); + attestationRegistry.attest(attestationPayload, attester); + + vm.prank(portal); + vm.expectEmit(true, true, true, true); + emit AttestationReplaced(attestation.attestationId, bytes32(abi.encode(2))); + attestationRegistry.replace(attestation.attestationId, attestationPayload, attester); + } + + function test_bulkReplace(AttestationPayload[2] memory attestationPayloads) public { + vm.assume(attestationPayloads[0].subject.length != 0); + vm.assume(attestationPayloads[0].attestationData.length != 0); + vm.assume(attestationPayloads[1].subject.length != 0); + vm.assume(attestationPayloads[1].attestationData.length != 0); + + SchemaRegistryMock schemaRegistryMock = SchemaRegistryMock(router.getSchemaRegistry()); + attestationPayloads[0].schemaId = schemaRegistryMock.getIdFromSchemaString("schemaString"); + attestationPayloads[1].schemaId = schemaRegistryMock.getIdFromSchemaString("schemaString"); + + Attestation memory attestation1 = _createAttestation(attestationPayloads[0], 1); + Attestation memory attestation2 = _createAttestation(attestationPayloads[1], 2); + + Attestation[] memory attestations = new Attestation[](2); + attestations[0] = attestation1; + attestations[1] = attestation2; + + AttestationPayload[] memory payloadsToAttest = new AttestationPayload[](2); + payloadsToAttest[0] = attestationPayloads[0]; + payloadsToAttest[1] = attestationPayloads[1]; + + bytes32[] memory attestationIds = new bytes32[](2); + attestationIds[0] = attestation1.attestationId; + attestationIds[1] = attestation2.attestationId; + + vm.startPrank(portal); + attestationRegistry.bulkAttest(payloadsToAttest, attester); + + attestationRegistry.bulkReplace(attestationIds, payloadsToAttest, attester); + } + + function test_bulkReplace_ArrayLengthMismatch(AttestationPayload[2] memory attestationPayloads) public { + vm.assume(attestationPayloads[0].subject.length != 0); + vm.assume(attestationPayloads[0].attestationData.length != 0); + vm.assume(attestationPayloads[1].subject.length != 0); + vm.assume(attestationPayloads[1].attestationData.length != 0); + + SchemaRegistryMock schemaRegistryMock = SchemaRegistryMock(router.getSchemaRegistry()); + attestationPayloads[0].schemaId = schemaRegistryMock.getIdFromSchemaString("schemaString"); + attestationPayloads[1].schemaId = schemaRegistryMock.getIdFromSchemaString("schemaString"); + + Attestation memory attestation1 = _createAttestation(attestationPayloads[0], 1); + Attestation memory attestation2 = _createAttestation(attestationPayloads[1], 2); + + Attestation[] memory attestations = new Attestation[](2); + attestations[0] = attestation1; + attestations[1] = attestation2; + + AttestationPayload[] memory payloadsToAttest = new AttestationPayload[](2); + payloadsToAttest[0] = attestationPayloads[0]; + payloadsToAttest[1] = attestationPayloads[1]; + + bytes32[] memory attestationIds = new bytes32[](3); + attestationIds[0] = attestation1.attestationId; + attestationIds[1] = attestation2.attestationId; + attestationIds[2] = bytes32(abi.encode(3)); + + vm.startPrank(portal); + attestationRegistry.bulkAttest(payloadsToAttest, attester); + + vm.expectRevert(AttestationRegistry.ArrayLengthMismatch.selector); + attestationRegistry.bulkReplace(attestationIds, payloadsToAttest, attester); + } + function test_revoke(AttestationPayload memory attestationPayload) public { vm.assume(attestationPayload.subject.length != 0); vm.assume(attestationPayload.attestationData.length != 0); @@ -192,20 +274,18 @@ contract AttestationRegistryTest is Test { assertEq(registeredAttestation.attestationId, bytes32(abi.encode(1))); assertFalse(registeredAttestation.revoked); assertEq(registeredAttestation.revocationDate, 0); - assertEq(registeredAttestation.replacedBy, bytes32(0)); assertEq(registeredAttestation.portal, portal); assertEq(registeredAttestation.attester, attester); vm.expectEmit(true, true, true, true); - emit AttestationRevoked(attestationId, bytes32(0)); - attestationRegistry.revoke(attestationId, bytes32(0)); + emit AttestationRevoked(attestationId); + attestationRegistry.revoke(attestationId); vm.stopPrank(); // Assert final state is a revoked attestation Attestation memory revokedAttestation = attestationRegistry.getAttestation(bytes32(abi.encode(1))); assertTrue(revokedAttestation.revoked); assertEq(revokedAttestation.revocationDate, block.timestamp); - assertEq(revokedAttestation.replacedBy, bytes32(0)); } function test_revoke_AttestationAlreadyRevoked(AttestationPayload memory attestationPayload) public { @@ -218,18 +298,18 @@ contract AttestationRegistryTest is Test { attestationRegistry.attest(attestationPayload, attester); vm.expectEmit(); - emit AttestationRevoked(bytes32(abi.encode(1)), bytes32(0)); - attestationRegistry.revoke(bytes32(abi.encode(1)), ""); + emit AttestationRevoked(bytes32(abi.encode(1))); + attestationRegistry.revoke(bytes32(abi.encode(1))); vm.expectRevert(AttestationRegistry.AlreadyRevoked.selector); - attestationRegistry.revoke(bytes32(abi.encode(1)), ""); + attestationRegistry.revoke(bytes32(abi.encode(1))); vm.stopPrank(); } function test_revoke_AttestationNotAttested() public { vm.expectRevert(AttestationRegistry.AttestationNotAttested.selector); - attestationRegistry.revoke(bytes32(abi.encode(1)), ""); + attestationRegistry.revoke(bytes32(abi.encode(1))); } function test_revoke_OnlyAttestingPortal(AttestationPayload memory attestationPayload) public { @@ -244,7 +324,7 @@ contract AttestationRegistryTest is Test { vm.expectRevert(AttestationRegistry.OnlyAttestingPortal.selector); vm.prank(user); - attestationRegistry.revoke(bytes32(abi.encode(1)), ""); + attestationRegistry.revoke(bytes32(abi.encode(1))); } function test_revoke_AttestationNotRevocable(AttestationPayload memory attestationPayload) public { @@ -268,42 +348,11 @@ contract AttestationRegistryTest is Test { attestationRegistry.attest(attestationPayload, attester); vm.expectRevert(AttestationRegistry.AttestationNotRevocable.selector); - attestationRegistry.revoke(bytes32(abi.encode(1)), ""); + attestationRegistry.revoke(bytes32(abi.encode(1))); vm.stopPrank(); } - function test_revokeAndReplace( - AttestationPayload memory attestationPayloadOrigin, - AttestationPayload memory attestationPayloadReplacement - ) public { - SchemaRegistryMock schemaRegistryMock = SchemaRegistryMock(router.getSchemaRegistry()); - vm.assume(attestationPayloadOrigin.subject.length != 0); - vm.assume(attestationPayloadOrigin.attestationData.length != 0); - attestationPayloadOrigin.schemaId = schemaRegistryMock.getIdFromSchemaString("schemaString"); - vm.assume(attestationPayloadReplacement.subject.length != 0); - vm.assume(attestationPayloadReplacement.attestationData.length != 0); - attestationPayloadReplacement.schemaId = schemaRegistryMock.getIdFromSchemaString("schemaString"); - schemaRegistryMock.createSchema("name", "description", "context", "schemaString"); - vm.startPrank(portal, attester); - - attestationRegistry.attest(attestationPayloadOrigin, attester); - attestationRegistry.attest(attestationPayloadReplacement, attester); - - // Do revert and replace the attestation - vm.expectEmit(true, true, true, true); - emit AttestationRevoked(bytes32(abi.encode(1)), bytes32(abi.encode(2))); - attestationRegistry.revoke(bytes32(abi.encode(1)), bytes32(abi.encode(2))); - - vm.stopPrank(); - - // Assert final state is a revoked and replaced attestation - Attestation memory revokedAttestation = attestationRegistry.getAttestation(bytes32(abi.encode(1))); - assertTrue(revokedAttestation.revoked); - assertEq(revokedAttestation.revocationDate, block.timestamp); - assertEq(revokedAttestation.replacedBy, bytes32(abi.encode(2))); - } - function test_bulkRevoke(AttestationPayload[3] memory attestationPayloads) public { vm.assume(attestationPayloads[0].subject.length != 0); vm.assume(attestationPayloads[0].attestationData.length != 0); @@ -335,7 +384,6 @@ contract AttestationRegistryTest is Test { assertEq(registeredAttestation1.attestationId, attestationId1); assertFalse(registeredAttestation1.revoked); assertEq(registeredAttestation1.revocationDate, 0); - assertEq(registeredAttestation1.replacedBy, bytes32(0)); assertEq(registeredAttestation1.portal, portal); assertEq(registeredAttestation1.attester, attester); @@ -344,7 +392,6 @@ contract AttestationRegistryTest is Test { assertEq(registeredAttestation2.attestationId, attestationId2); assertFalse(registeredAttestation2.revoked); assertEq(registeredAttestation2.revocationDate, 0); - assertEq(registeredAttestation2.replacedBy, bytes32(0)); assertEq(registeredAttestation2.portal, portal); assertEq(registeredAttestation2.attester, attester); @@ -356,37 +403,21 @@ contract AttestationRegistryTest is Test { replacingAttestations[1] = attestationId3; vm.expectEmit(true, true, true, true); - emit AttestationRevoked(attestationsToRevoke[0], replacingAttestations[0]); + emit AttestationRevoked(attestationsToRevoke[0]); vm.expectEmit(true, true, true, true); - emit AttestationRevoked(attestationsToRevoke[1], replacingAttestations[1]); + emit AttestationRevoked(attestationsToRevoke[1]); - attestationRegistry.bulkRevoke(attestationsToRevoke, replacingAttestations); + attestationRegistry.bulkRevoke(attestationsToRevoke); vm.stopPrank(); // Assert final state is a revoked attestation and a replaced attestation Attestation memory revokedAttestation1 = attestationRegistry.getAttestation(attestationId1); assertTrue(revokedAttestation1.revoked); assertEq(revokedAttestation1.revocationDate, block.timestamp); - assertEq(revokedAttestation1.replacedBy, bytes32(0)); Attestation memory revokedAttestation2 = attestationRegistry.getAttestation(attestationId2); assertTrue(revokedAttestation2.revoked); assertEq(revokedAttestation2.revocationDate, block.timestamp); - assertEq(revokedAttestation2.replacedBy, attestationId3); - } - - function test_bulkRevoke_InvalidParams() public { - bytes32 attestationId1 = bytes32(abi.encode(1)); - bytes32 attestationId2 = bytes32(abi.encode(2)); - - bytes32[] memory attestationsToRevoke = new bytes32[](2); - attestationsToRevoke[0] = attestationId1; - attestationsToRevoke[1] = attestationId2; - bytes32[] memory replacingAttestations = new bytes32[](1); - replacingAttestations[0] = bytes32(0); - - vm.expectRevert(AttestationRegistry.BulkRevocationInvalidParams.selector); - attestationRegistry.bulkRevoke(attestationsToRevoke, replacingAttestations); } function test_isRevocable() public { diff --git a/test/example/EASPortal.t.sol b/test/example/EASPortal.t.sol index f72e29bc..3205b6b4 100644 --- a/test/example/EASPortal.t.sol +++ b/test/example/EASPortal.t.sol @@ -139,7 +139,7 @@ contract EASPortalTest is Test { function test_revoke() public { vm.expectRevert(EASPortal.NoRevocation.selector); - easPortal.revoke(bytes32(uint256(1)), bytes32(uint256(1))); + easPortal.revoke(bytes32(uint256(1))); } function test_bulkRevoke() public { @@ -152,7 +152,7 @@ contract EASPortalTest is Test { replacingAttestations[1] = bytes32(uint256(3)); vm.expectRevert(EASPortal.NoBulkRevocation.selector); - easPortal.bulkRevoke(attestationsToRevoke, replacingAttestations); + easPortal.bulkRevoke(attestationsToRevoke); } function testSupportsInterface() public { diff --git a/test/mocks/AttestationRegistryMock.sol b/test/mocks/AttestationRegistryMock.sol index 9f27511e..c7d09434 100644 --- a/test/mocks/AttestationRegistryMock.sol +++ b/test/mocks/AttestationRegistryMock.sol @@ -9,9 +9,10 @@ contract AttestationRegistryMock { mapping(bytes32 attestationId => Attestation attestation) private attestations; event AttestationRegistered(); + event AttestationReplaced(); event BulkAttestationsRegistered(); - event AttestationRevoked(bytes32 attestationId, bytes32 replacedBy); - event BulkAttestationsRevoked(bytes32[] attestationId, bytes32[] replacedBy); + event AttestationRevoked(bytes32 attestationId); + event BulkAttestationsRevoked(bytes32[] attestationId); function test() public {} @@ -46,14 +47,28 @@ contract AttestationRegistryMock { emit BulkAttestationsRegistered(); } - function revoke(bytes32 attestationId, bytes32 replacedBy) public { + function replace(bytes32 /*attestationId*/, AttestationPayload calldata attestationPayload, address attester) public { + require(bytes32(attestationPayload.schemaId) != 0, "Invalid attestationPayload"); + require(attester != address(0), "Invalid attester"); + + emit AttestationRegistered(); + emit AttestationReplaced(); + } + + function bulkReplace( + bytes32[] calldata attestationId, + AttestationPayload[] calldata attestationPayload, + address attester + ) public {} + + function revoke(bytes32 attestationId) public { require(bytes32(attestationId) != 0, "Invalid attestation"); - emit AttestationRevoked(attestationId, replacedBy); + emit AttestationRevoked(attestationId); } - function bulkRevoke(bytes32[] memory attestationIds, bytes32[] memory replacedBy) public { + function bulkRevoke(bytes32[] memory attestationIds) public { require(attestationIds.length > 0, "Invalid attestation"); - emit BulkAttestationsRevoked(attestationIds, replacedBy); + emit BulkAttestationsRevoked(attestationIds); } function getVersionNumber() public view returns (uint16) { diff --git a/test/mocks/ValidPortalMock.sol b/test/mocks/ValidPortalMock.sol index 89d9b6ef..a9480651 100644 --- a/test/mocks/ValidPortalMock.sol +++ b/test/mocks/ValidPortalMock.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.21; import { AbstractPortal } from "../../src/interface/AbstractPortal.sol"; -import { AttestationPayload } from "../../src/types/Structs.sol"; contract ValidPortalMock is AbstractPortal { function test() public {} diff --git a/test/portal/DefaultPortal.t.sol b/test/portal/DefaultPortal.t.sol index 4d59d948..e601cf6f 100644 --- a/test/portal/DefaultPortal.t.sol +++ b/test/portal/DefaultPortal.t.sol @@ -28,8 +28,8 @@ contract DefaultPortalTest is Test { event BulkAttestationsRegistered(); event ModulesRunForAttestation(); event ModulesBulkRunForAttestation(); - event AttestationRevoked(bytes32 attestationId, bytes32 replacedBy); - event BulkAttestationsRevoked(bytes32[] attestationId, bytes32[] replacedBy); + event AttestationRevoked(bytes32 attestationId); + event BulkAttestationsRevoked(bytes32[] attestationId); function setUp() public { modules.push(address(correctModule)); @@ -106,6 +106,46 @@ contract DefaultPortalTest is Test { defaultPortal.bulkAttest(payloadsToAttest, validationPayloads); } + function test_replace() public { + // Create attestation payload + AttestationPayload memory attestationPayload = AttestationPayload( + bytes32(uint256(1)), + uint64(block.timestamp + 1 days), + bytes("subject"), + new bytes(1) + ); + // Create validation payload + bytes[] memory validationPayload = new bytes[](2); + vm.expectEmit(true, true, true, true); + emit AttestationRegistered(); + defaultPortal.attest(attestationPayload, validationPayload); + defaultPortal.replace(bytes32(abi.encode(1)), attestationPayload, validationPayload); + } + + function test_bulkReplace(AttestationPayload[2] memory attestationPayloads) public { + vm.assume(bytes32(attestationPayloads[0].schemaId) != 0); + vm.assume(bytes32(attestationPayloads[1].schemaId) != 0); + // Create attestations payloads + AttestationPayload[] memory payloadsToAttest = new AttestationPayload[](2); + payloadsToAttest[0] = attestationPayloads[0]; + payloadsToAttest[1] = attestationPayloads[1]; + + // Create validation payloads + bytes[] memory validationPayload1 = new bytes[](1); + bytes[] memory validationPayload2 = new bytes[](1); + + bytes[][] memory validationPayloads = new bytes[][](2); + validationPayloads[0] = validationPayload1; + validationPayloads[1] = validationPayload2; + + bytes32[] memory attestationIds = new bytes32[](2); + attestationIds[0] = bytes32(abi.encode(1)); + attestationIds[1] = bytes32(abi.encode(2)); + + defaultPortal.bulkAttest(payloadsToAttest, validationPayloads); + defaultPortal.bulkReplace(attestationIds, payloadsToAttest, validationPayloads); + } + function test_revoke_byPortalOwner() public { // Create attestation payload AttestationPayload memory attestationPayload = AttestationPayload( @@ -127,8 +167,8 @@ contract DefaultPortalTest is Test { // Revoke the attestation as portal owner vm.prank(portalOwner); vm.expectEmit(true, true, true, true); - emit AttestationRevoked(bytes32(abi.encode(1)), bytes32(0)); - defaultPortal.revoke(bytes32(abi.encode(1)), ""); + emit AttestationRevoked(bytes32(abi.encode(1))); + defaultPortal.revoke(bytes32(abi.encode(1))); } function test_revokeFail_OnlyOwner() public { @@ -152,20 +192,18 @@ contract DefaultPortalTest is Test { // Revoke the attestation as a random user vm.prank(makeAddr("random")); vm.expectRevert(AbstractPortal.OnlyPortalOwner.selector); - defaultPortal.revoke(bytes32(abi.encode(1)), ""); + defaultPortal.revoke(bytes32(abi.encode(1))); } function test_bulkRevoke() public { bytes32[] memory attestationsToRevoke = new bytes32[](2); attestationsToRevoke[0] = bytes32("1"); attestationsToRevoke[1] = bytes32("2"); - bytes32[] memory replacingAttestations = new bytes32[](2); - replacingAttestations[0] = bytes32(0); - replacingAttestations[1] = bytes32(0); vm.expectEmit(true, true, true, true); - emit BulkAttestationsRevoked(attestationsToRevoke, replacingAttestations); - defaultPortal.bulkRevoke(attestationsToRevoke, replacingAttestations); + emit BulkAttestationsRevoked(attestationsToRevoke); + vm.prank(portalOwner); + defaultPortal.bulkRevoke(attestationsToRevoke); } function testSupportsInterface() public {