Skip to content

Commit

Permalink
Add global hook, fix test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
boyuanx committed Jan 26, 2024
1 parent 086ba7f commit ee90c5b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 20 deletions.
30 changes: 26 additions & 4 deletions src/core/SP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.20;

import { ISP } from "../interfaces/ISP.sol";
import { ISPHook } from "../interfaces/ISPHook.sol";
import { ISPGlobalHook } from "../interfaces/ISPGlobalHook.sol";
import { Schema } from "../models/Schema.sol";
import { Attestation, OffchainAttestation } from "../models/Attestation.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
Expand All @@ -20,6 +21,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
mapping(string => OffchainAttestation) _offchainAttestationRegistry;
uint256 schemaCounter;
uint256 attestationCounter;
ISPGlobalHook globalHook;
}

// keccak256(abi.encode(uint256(keccak256("ethsign.SP")) - 1)) & ~bytes32(uint256(0xff))
Expand All @@ -42,9 +44,6 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
if (block.chainid == 7001) {
initialize(); // Special case for ZetaChain, where Foundry scripting fails
}
if (block.chainid != 31_337) {
_disableInitializers();
}
Expand All @@ -58,14 +57,16 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
}

function register(Schema calldata schema) external override returns (uint256 schemaId) {
return _register(schema);
schemaId = _register(schema);
_callGlobalHook();
}

function registerBatch(Schema[] calldata schemas) external override returns (uint256[] memory schemaIds) {
schemaIds = new uint256[](schemas.length);
for (uint256 i = 0; i < schemas.length; i++) {
schemaIds[i] = _register(schemas[i]);
}
_callGlobalHook();
}

function attest(
Expand All @@ -85,6 +86,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
(uint256 schemaId, uint256 attestationId) = _attest(attestation, indexingKey, delegateMode);
ISPHook hook = __getResolverFromAttestationId(attestationId);
if (address(hook) != address(0)) hook.didReceiveAttestation(_msgSender(), schemaId, attestationId, extraData);
_callGlobalHook();
return attestationId;
}

Expand Down Expand Up @@ -115,6 +117,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
hook.didReceiveAttestation(_msgSender(), schemaId, attestationId, extraData);
}
}
_callGlobalHook();
}

function attest(
Expand All @@ -137,6 +140,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
if (address(hook) != address(0)) {
hook.didReceiveAttestation{ value: resolverFeesETH }(_msgSender(), schemaId, attestationId, extraData);
}
_callGlobalHook();
return attestationId;
}

Expand Down Expand Up @@ -171,6 +175,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
);
}
}
_callGlobalHook();
}

function attest(
Expand All @@ -196,6 +201,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
_msgSender(), schemaId, attestationId, resolverFeesERC20Token, resolverFeesERC20Amount, extraData
);
}
_callGlobalHook();
return attestationId;
}

Expand Down Expand Up @@ -237,6 +243,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
);
}
}
_callGlobalHook();
}

function attestOffchain(
Expand All @@ -255,6 +262,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
attester = delegateAttester;
}
_attestOffchain(offchainAttestationId, attester);
_callGlobalHook();
}

function attestOffchainBatch(
Expand All @@ -275,6 +283,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
for (uint256 i = 0; i < attestationIds.length; i++) {
_attestOffchain(attestationIds[i], attester);
}
_callGlobalHook();
}

function revoke(
Expand All @@ -299,6 +308,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
if (address(hook) != address(0)) {
hook.didReceiveRevocation(_msgSender(), schemaId, attestationId, extraData);
}
_callGlobalHook();
}

function revokeBatch(
Expand Down Expand Up @@ -328,6 +338,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
hook.didReceiveRevocation(_msgSender(), schemaId, attestationIds[i], extraData);
}
}
_callGlobalHook();
}

function revoke(
Expand All @@ -351,6 +362,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
if (address(hook) != address(0)) {
hook.didReceiveRevocation{ value: resolverFeesETH }(_msgSender(), schemaId, attestationId, extraData);
}
_callGlobalHook();
}

function revokeBatch(
Expand Down Expand Up @@ -384,6 +396,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
);
}
}
_callGlobalHook();
}

function revoke(
Expand All @@ -409,6 +422,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
_msgSender(), schemaId, attestationId, resolverFeesERC20Token, resolverFeesERC20Amount, extraData
);
}
_callGlobalHook();
}

function revokeBatch(
Expand Down Expand Up @@ -447,6 +461,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
);
}
}
_callGlobalHook();
}

function revokeOffchain(
Expand All @@ -465,6 +480,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
);
}
_revokeOffchain(offchainAttestationId, reason, delegateMode);
_callGlobalHook();
}

function revokeOffchainBatch(
Expand All @@ -491,6 +507,7 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
}
_revokeOffchain(offchainAttestationIds[i], reasons[i], delegateMode);
}
_callGlobalHook();
}

function getSchema(uint256 schemaId) external view override returns (Schema memory) {
Expand Down Expand Up @@ -571,6 +588,11 @@ contract SP is ISP, UUPSUpgradeable, OwnableUpgradeable {
return keccak256(abi.encode(REVOKE_OFFCHAIN_BATCH_ACTION_NAME, offchainAttestationIds));
}

function _callGlobalHook() internal {
SPStorage storage $ = _getSPStorage();
if (address($.globalHook) != address(0)) $.globalHook.callHook(_msgData());
}

function _register(Schema calldata schema) internal returns (uint256 schemaId) {
SPStorage storage $ = _getSPStorage();
schemaId = $.schemaCounter++;
Expand Down
6 changes: 6 additions & 0 deletions src/interfaces/ISPGlobalHook.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface ISPGlobalHook {
function callHook(bytes calldata msgData) external;
}
33 changes: 17 additions & 16 deletions test/SP.test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { MockResolver } from "../src/mock/MockResolver.sol";
import { Schema } from "../src/models/Schema.sol";
import { DataLocation } from "../src/models/DataLocation.sol";
import { Attestation, OffchainAttestation } from "../src/models/Attestation.sol";
import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

contract SPTest is Test {
ISP public sp;
Expand Down Expand Up @@ -224,7 +225,7 @@ contract SPTest is Test {
(address signer, uint256 signerPk) = makeAddrAndKey("signer");
attestations[0].attester = signer;
bytes32 hash = sp.getDelegatedAttestHash(attestations[0]);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
// Delegate attest batch
sp.attest(attestations[0], indexingKeys[0], _vrsToSignature(v, r, s), "");
Attestation memory attestation0Actual = sp.getAttestation(attestationId0);
Expand Down Expand Up @@ -255,7 +256,7 @@ contract SPTest is Test {
attestations[0].attester = signer;
attestations[1].attester = signer;
bytes32 hash = sp.getDelegatedAttestBatchHash(attestations);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
// Delegate attest batch
sp.attestBatch(attestations, indexingKeys, _vrsToSignature(v, r, s), "");
Attestation memory attestation0Actual = sp.getAttestation(attestationId0);
Expand All @@ -271,14 +272,14 @@ contract SPTest is Test {
// Altering the first reference attester, should revert with `InvalidDelegateSignature`
attestations[0].attester = prankSender;
hash = sp.getDelegatedAttestBatchHash(attestations);
(v, r, s) = vm.sign(signerPk, hash);
(v, r, s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
vm.expectRevert(abi.encodeWithSelector(InvalidDelegateSignature.selector));
sp.attestBatch(attestations, indexingKeys, _vrsToSignature(v, r, s), "");
attestations[0].attester = signer;
// Altering the second attester, should fail attester consistency check
attestations[1].attester = prankSender;
hash = sp.getDelegatedAttestBatchHash(attestations);
(v, r, s) = vm.sign(signerPk, hash);
(v, r, s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
vm.expectRevert(abi.encodeWithSelector(AttestationWrongAttester.selector, signer, prankSender));
sp.attestBatch(attestations, indexingKeys, _vrsToSignature(v, r, s), "");
}
Expand All @@ -287,7 +288,7 @@ contract SPTest is Test {
string[] memory offchainAttestationIds = _createMockAttestationIds();
(address signer, uint256 signerPk) = makeAddrAndKey("signer");
bytes32 hash = sp.getDelegatedOffchainAttestHash(offchainAttestationIds[0]);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
sp.attestOffchain(offchainAttestationIds[0], signer, _vrsToSignature(v, r, s));
OffchainAttestation memory offchainAttestation = sp.getOffchainAttestation(offchainAttestationIds[0]);
assertEq(offchainAttestation.attester, signer);
Expand All @@ -302,7 +303,7 @@ contract SPTest is Test {
string[] memory offchainAttestationIds = _createMockAttestationIds();
(address signer, uint256 signerPk) = makeAddrAndKey("signer");
bytes32 hash = sp.getDelegatedOffchainAttestBatchHash(offchainAttestationIds);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
// Try to fail on purpose first
vm.expectRevert(abi.encodeWithSelector(InvalidDelegateSignature.selector));
sp.attestOffchainBatch(offchainAttestationIds, prankSender, _vrsToSignature(v, r, s));
Expand All @@ -323,15 +324,15 @@ contract SPTest is Test {
(address signer, uint256 signerPk) = makeAddrAndKey("signer");
attestations[0].attester = signer;
bytes32 hash = sp.getDelegatedAttestHash(attestations[0]);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
sp.attest(attestations[0], indexingKeys[0], _vrsToSignature(v, r, s), "");
// Delegated revoke
// Try to fail on purpose first
vm.expectRevert(abi.encodeWithSelector(InvalidDelegateSignature.selector));
sp.revoke(attestationId0, "", _vrsToSignature(v, r, s), ""); // Still using the attest signature
// Revoke correctly
hash = sp.getDelegatedRevokeHash(attestationId0);
(v, r, s) = vm.sign(signerPk, hash);
(v, r, s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
sp.revoke(attestationId0, "", _vrsToSignature(v, r, s), "");
}

Expand All @@ -350,41 +351,41 @@ contract SPTest is Test {
attestations[0].attester = signer;
attestations[1].attester = signer;
bytes32 hash = sp.getDelegatedAttestBatchHash(attestations);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
sp.attestBatch(attestations, indexingKeys, _vrsToSignature(v, r, s), "");
// Revoke batch
// Sign using the wrong signer first
(, uint256 signerPk1) = makeAddrAndKey("signer1");
hash = sp.getDelegatedRevokeBatchHash(attestationIds);
(v, r, s) = vm.sign(signerPk1, hash);
(v, r, s) = vm.sign(signerPk1, MessageHashUtils.toEthSignedMessageHash(hash));
vm.expectRevert(abi.encodeWithSelector(InvalidDelegateSignature.selector));
sp.revokeBatch(attestationIds, _createMockReasons(), _vrsToSignature(v, r, s), "");
// Revoke correctly with the correct signer
(v, r, s) = vm.sign(signerPk, hash);
(v, r, s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
sp.revokeBatch(attestationIds, _createMockReasons(), _vrsToSignature(v, r, s), "");
}

function test_revoke_offchain_delegated() public {
string[] memory offchainAttestationIds = _createMockAttestationIds();
(address signer, uint256 signerPk) = makeAddrAndKey("signer");
bytes32 hash = sp.getDelegatedOffchainAttestHash(offchainAttestationIds[0]);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
vm.warp(100);
sp.attestOffchain(offchainAttestationIds[0], signer, _vrsToSignature(v, r, s));
// Try to fail on purpose first
vm.expectRevert(abi.encodeWithSelector(InvalidDelegateSignature.selector));
sp.revokeOffchain(offchainAttestationIds[0], "", _vrsToSignature(v, r, s));
// Revoke correctly
hash = sp.getDelegatedOffchainRevokeHash(offchainAttestationIds[0]);
(v, r, s) = vm.sign(signerPk, hash);
(v, r, s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
sp.revokeOffchain(offchainAttestationIds[0], "", _vrsToSignature(v, r, s));
}

function test_revoke_offchain_batch_delegated() public {
string[] memory offchainAttestationIds = _createMockAttestationIds();
(address signer, uint256 signerPk) = makeAddrAndKey("signer");
bytes32 hash = sp.getDelegatedOffchainAttestBatchHash(offchainAttestationIds);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
vm.warp(100);
sp.attestOffchainBatch(offchainAttestationIds, signer, _vrsToSignature(v, r, s));
vm.prank(prankSender);
Expand All @@ -397,13 +398,13 @@ contract SPTest is Test {
string memory offchainAttestationId1 = offchainAttestationIds[1];
offchainAttestationIds[1] = offchainAttestationIdPranked;
hash = sp.getDelegatedOffchainRevokeBatchHash(offchainAttestationIds);
(v, r, s) = vm.sign(signerPk, hash);
(v, r, s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
vm.expectRevert(abi.encodeWithSelector(AttestationWrongAttester.selector, prankSender, signer));
sp.revokeOffchainBatch(offchainAttestationIds, _createMockReasons(), _vrsToSignature(v, r, s));
// Revoke correctly
offchainAttestationIds[1] = offchainAttestationId1;
hash = sp.getDelegatedOffchainRevokeBatchHash(offchainAttestationIds);
(v, r, s) = vm.sign(signerPk, hash);
(v, r, s) = vm.sign(signerPk, MessageHashUtils.toEthSignedMessageHash(hash));
sp.revokeOffchainBatch(offchainAttestationIds, _createMockReasons(), _vrsToSignature(v, r, s));
}

Expand Down

0 comments on commit ee90c5b

Please sign in to comment.