From 471d3be6f595aa6d3b15ce3e14c24827e9155d84 Mon Sep 17 00:00:00 2001 From: Ben DiFrancesco Date: Mon, 5 Feb 2024 13:54:16 -0500 Subject: [PATCH] Correct the registry domain separator and refactor test suite (#7) * Test internal variable initialization in registry constructor tests * Improvements to domain separator test * Improvements to the RegisteryKeys tests * Naming improvements to registerKeysOnBehalf tests * Update test imports and remove duplicated import * Refactor signature test helpers for test simplicity * Simplify mock contract for ERC1271 signatures * Remove redundant test and add missing ones for register on behalf * Minor cleanup to increment nonce test * Update the EIP712Domain separator to match the spec * Simplify Announcer test names * Use `vm.expectRevert("")` instead of `vm.expectRevert()` --- src/ERC6538Registry.sol | 2 +- test/ERC5564Announcer.t.sol | 4 +- test/ERC6538Registry.t.sol | 552 ++++++++++++++---------------------- 3 files changed, 217 insertions(+), 341 deletions(-) diff --git a/src/ERC6538Registry.sol b/src/ERC6538Registry.sol index 1c1433c..eca4825 100644 --- a/src/ERC6538Registry.sol +++ b/src/ERC6538Registry.sol @@ -131,7 +131,7 @@ contract ERC6538Registry { return keccak256( abi.encode( keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address registryContract)" + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256("ERC6538Registry"), keccak256("1.0"), diff --git a/test/ERC5564Announcer.t.sol b/test/ERC5564Announcer.t.sol index ae01a3f..d5e3afc 100644 --- a/test/ERC5564Announcer.t.sol +++ b/test/ERC5564Announcer.t.sol @@ -19,7 +19,7 @@ contract ERC5564AnnouncerTest is Test, Deploy { } contract Announce is ERC5564AnnouncerTest { - function testFuzz_EmitsAnnouncementEventForAnyCaller( + function testFuzz_EmitsAnnouncementEvent( uint256 schemeId, address stealthAddress, address caller, @@ -38,7 +38,7 @@ contract Announce is ERC5564AnnouncerTest { /// forge-config: default.fuzz.runs = 1 /// forge-config: ci.fuzz.runs = 1 /// forge-config: lite.fuzz.runs = 1 - function testFuzz_AlwaysSucceedsForAnyCallerThatCallsTheAnnounceFunction( + function testFuzz_AlwaysSucceeds( uint256 schemeId, address stealthAddress, address caller, diff --git a/test/ERC6538Registry.t.sol b/test/ERC6538Registry.t.sol index 5592bf6..7608175 100644 --- a/test/ERC6538Registry.t.sol +++ b/test/ERC6538Registry.t.sol @@ -3,10 +3,9 @@ pragma solidity 0.8.23; import {Test} from "forge-std/Test.sol"; import {Deploy} from "script/Deploy.s.sol"; +import {ERC6538Registry, IERC1271} from "src/ERC6538Registry.sol"; contract ERC6538RegistryTest is Test, Deploy { - SigUtils internal sigUtils; - error ERC6538Registry__InvalidSignature(); event StealthMetaAddressSet( @@ -15,31 +14,77 @@ contract ERC6538RegistryTest is Test, Deploy { function setUp() public { Deploy.run(); - sigUtils = new SigUtils(registry.DOMAIN_SEPARATOR()); + } + + // Test helper to compute EIP712 domain separator for a given chain id and deployment address + function _computeDomainSeparator(uint256 _chainId, address _registryAddress) + public + pure + returns (bytes32) + { + return keccak256( + abi.encode( + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ), + keccak256("ERC6538Registry"), + keccak256("1.0"), + _chainId, + _registryAddress + ) + ); + } + + function _generateRegistrationSignature( + uint256 _registrantPrivateKey, + uint256 _schemeId, + bytes memory _stealthMetaAddress, + uint256 _nonce + ) public view returns (bytes memory _signature) { + bytes32 _dataHash = keccak256( + abi.encode(registry.ERC6538REGISTRY_ENTRY_TYPE_HASH(), _schemeId, _stealthMetaAddress, _nonce) + ); + bytes32 _hash = keccak256(abi.encodePacked("\x19\x01", registry.DOMAIN_SEPARATOR(), _dataHash)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(_registrantPrivateKey, _hash); + _signature = abi.encodePacked(r, s, v); + } + + function _notVmOrConsole(address _address) public pure { + // This function is used to avoid calling the VM or the console, which would return error + // messages. It is used in tests where we want assert a function reverts without a message. + vm.assume(_address != address(vm)); + vm.assume(_address != address(address(0x000000000000000000636F6e736F6c652e6c6f67))); + } +} + +// Test harness to expose internal contract methods for test purpose only +contract ERC6538RegistryHarness is ERC6538Registry { + function exposed_INITIAL_CHAIN_ID() public view returns (uint256) { + return INITIAL_CHAIN_ID; + } + + function exposed_INITIAL_DOMAIN_SEPARATOR() public view returns (bytes32) { + return INITIAL_DOMAIN_SEPARATOR; } } contract Constructor is ERC6538RegistryTest { - function test_TheDomainSeparatorIsInitializedCorrectly() external { + function test_SetsTheInitialChainId() external { + ERC6538RegistryHarness _registry = new ERC6538RegistryHarness(); + assertEq(_registry.exposed_INITIAL_CHAIN_ID(), block.chainid); + } + + function test_SetsTheInitialDomainSeparator() external { + ERC6538RegistryHarness _registry = new ERC6538RegistryHarness(); assertEq( - registry.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address registryContract)" - ), - keccak256("ERC6538Registry"), - keccak256("1.0"), - block.chainid, - address(registry) - ) - ) + _registry.exposed_INITIAL_DOMAIN_SEPARATOR(), + _computeDomainSeparator(block.chainid, address(_registry)) ); } } contract RegisterKeys is ERC6538RegistryTest { - function testFuzz_EmitsStealthMetaAddressSetEventForAnyCaller( + function testFuzz_EmitsASetEvent( address caller, uint256 schemeId, bytes memory stealthMetaAddress @@ -50,24 +95,39 @@ contract RegisterKeys is ERC6538RegistryTest { registry.registerKeys(schemeId, stealthMetaAddress); } - function testFuzz_MapsRegistrantToSchemeIdToStealthMetaAddressInStorageCorrectlyForAnyCaller( + function testFuzz_SetsTheStealthMetaAddressForANewRegistrant( address caller, uint256 schemeId, bytes memory stealthMetaAddress ) external { - assertEq(registry.stealthMetaAddressOf((caller), schemeId), ""); vm.prank(caller); registry.registerKeys(schemeId, stealthMetaAddress); + assertEq(registry.stealthMetaAddressOf((caller), schemeId), stealthMetaAddress); } + function testFuzz_UpdatesTheStealthMetaAddressForAnExistingRegistrant( + address caller, + uint256 schemeId, + bytes memory stealthMetaAddress1, + bytes memory stealthMetaAddress2 + ) external { + vm.prank(caller); + registry.registerKeys(schemeId, stealthMetaAddress1); + assertEq(registry.stealthMetaAddressOf((caller), schemeId), stealthMetaAddress1); + + vm.prank(caller); + registry.registerKeys(schemeId, stealthMetaAddress2); + assertEq(registry.stealthMetaAddressOf((caller), schemeId), stealthMetaAddress2); + } + // This test is a subset of `testFuzz_EmitsStealthMetaAddressSetEvent`, and is mainly present to // make the `announce` method's specification more explicit. For this reason, we set the number of // runs to 1 for all profiles. /// forge-config: default.fuzz.runs = 1 /// forge-config: ci.fuzz.runs = 1 /// forge-config: lite.fuzz.runs = 1 - function testFuzz_AlwaysSucceedsForACallerThatCallsTheRegisterKeysFunction( + function testFuzz_AlwaysSucceeds( address caller, uint256 schemeId, bytes memory stealthMetaAddress @@ -79,432 +139,248 @@ contract RegisterKeys is ERC6538RegistryTest { contract RegisterKeysOnBehalf is ERC6538RegistryTest { function testFuzz_SetsTheStealthMetaAddressForANewRegistrantWhenProvidedAValidErc712Signature( - string memory name, + string memory registrantSeed, uint256 schemeId, bytes memory stealthMetaAddress ) external { - (address alice, uint256 alicePk) = makeAddrAndKey(name); - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, 0 /* nonce */ ); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - registry.registerKeysOnBehalf(alice, schemeId, signature, stealthMetaAddress); - assertEq(registry.stealthMetaAddressOf(alice, schemeId), stealthMetaAddress); - } - - function testFuzz_EmitsAStealthMetaAddressSetEventForANewRegistrantWhenProvidedAValidErc712Signature( - string memory name, - uint256 schemeId, - bytes memory stealthMetaAddress - ) external { - (address alice, uint256 alicePk) = makeAddrAndKey(name); - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, 0 /* nonce */ ); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - vm.expectEmit(true, true, true, true); - emit StealthMetaAddressSet(alice, schemeId, stealthMetaAddress); - registry.registerKeysOnBehalf(alice, schemeId, signature, stealthMetaAddress); - } - - function testFuzz_SetsTheStealthMetaAddressForANewRegistrantWhenProvidedAValidErc1271Signature( - string memory name, - uint256 schemeId, - bytes memory stealthMetaAddress - ) external { - (address alice, uint256 alicePk) = makeAddrAndKey(name); - - vm.prank(alice); - ERC1271MockContract erc1271MockContract = new ERC1271MockContract(); - address registrant = address(erc1271MockContract); - - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, 0 /* nonce */ ); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); + (address registrant, uint256 registrantPrivateKey) = makeAddrAndKey(registrantSeed); + bytes memory signature = + _generateRegistrationSignature(registrantPrivateKey, schemeId, stealthMetaAddress, 0); registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); assertEq(registry.stealthMetaAddressOf(registrant, schemeId), stealthMetaAddress); } - function testFuzz_EmitsAStealthMetaAddressSetEventForANewRegistrantWhenProvidedAValidErc1271Signature( - string memory name, + function testFuzz_EmitsAStealthMetaAddressSetEventForANewRegistrantWhenProvidedAValidErc712Signature( + string memory registrantSeed, uint256 schemeId, bytes memory stealthMetaAddress ) external { - (address alice, uint256 alicePk) = makeAddrAndKey(name); - - vm.prank(alice); - ERC1271MockContract erc1271MockContract = new ERC1271MockContract(); - address registrant = address(erc1271MockContract); - - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, 0 /* nonce */ ); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); + (address registrant, uint256 registrantPrivateKey) = makeAddrAndKey(registrantSeed); + bytes memory signature = + _generateRegistrationSignature(registrantPrivateKey, schemeId, stealthMetaAddress, 0); - vm.expectEmit(true, true, true, true); + vm.expectEmit(); emit StealthMetaAddressSet(registrant, schemeId, stealthMetaAddress); registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); } - function testFuzz_SetsTheStealthMetaAddressForTheSameRegistrantAtLeastTwiceWhenProvidedAValidErc712Signature( - string memory name, + function testFuzz_SetsTheStealthMetaAddressForANewRegistrantWhenProvidedAValidErc1271Signature( uint256 schemeId, bytes memory stealthMetaAddress, - uint256 numOfUpdates + bytes memory signature ) external { - numOfUpdates = bound(numOfUpdates, 1, 50); - (address alice, uint256 alicePk) = makeAddrAndKey(name); + MockERC1271Signer mockRegistrant = new MockERC1271Signer(); + mockRegistrant.setResponse__isValidSignature(true); - for (uint256 nonce = 0; nonce < numOfUpdates; nonce++) { - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, nonce); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - registry.registerKeysOnBehalf(alice, schemeId, signature, stealthMetaAddress); - assertEq(registry.stealthMetaAddressOf(alice, schemeId), stealthMetaAddress); - } + registry.registerKeysOnBehalf(address(mockRegistrant), schemeId, signature, stealthMetaAddress); + assertEq(registry.stealthMetaAddressOf(address(mockRegistrant), schemeId), stealthMetaAddress); } - function testFuzz_EmitsStealthMetaAddressSetEventsForTheSameRegistrantAtLeastTwiceWhenProvidedAValidErc712Signature( - string memory name, + function testFuzz_EmitsAStealthMetaAddressSetEventForANewRegistrantWhenProvidedAValidErc1271Signature( uint256 schemeId, bytes memory stealthMetaAddress, - uint256 numOfUpdates + bytes memory signature ) external { - numOfUpdates = bound(numOfUpdates, 1, 50); - (address alice, uint256 alicePk) = makeAddrAndKey(name); + MockERC1271Signer mockRegistrant = new MockERC1271Signer(); + mockRegistrant.setResponse__isValidSignature(true); - for (uint256 nonce = 0; nonce < numOfUpdates; nonce++) { - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, nonce); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - vm.expectEmit(true, true, true, true); - emit StealthMetaAddressSet(alice, schemeId, stealthMetaAddress); - registry.registerKeysOnBehalf(alice, schemeId, signature, stealthMetaAddress); - } + vm.expectEmit(); + emit StealthMetaAddressSet(address(mockRegistrant), schemeId, stealthMetaAddress); + registry.registerKeysOnBehalf(address(mockRegistrant), schemeId, signature, stealthMetaAddress); } - function testFuzz_SetsTheStealthMetaAddressForTheSameRegistrantAtLeastTwiceWhenProvidedAValidErc1271Signature( - string memory name, + function testFuzz_UpdatesTheStealthMetaAddressForAnExistingRegistrantWhenProvidedAValidErc712Signature( + string memory registrantSeed, uint256 schemeId, bytes memory stealthMetaAddress, uint256 numOfUpdates ) external { - numOfUpdates = bound(numOfUpdates, 1, 50); - - (address alice, uint256 alicePk) = makeAddrAndKey(name); - - vm.prank(alice); - ERC1271MockContract erc1271MockContract = new ERC1271MockContract(); + numOfUpdates = bound(numOfUpdates, 2, 50); + (address registrant, uint256 registrantPrivateKey) = makeAddrAndKey(registrantSeed); for (uint256 nonce = 0; nonce < numOfUpdates; nonce++) { - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, nonce); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, sigUtils.getTypedDataHash(registrantInfo)); - bytes memory signature = abi.encodePacked(r, s, v); + bytes memory signature = + _generateRegistrationSignature(registrantPrivateKey, schemeId, stealthMetaAddress, nonce); - registry.registerKeysOnBehalf( - address(erc1271MockContract), schemeId, signature, stealthMetaAddress - ); - assertEq( - registry.stealthMetaAddressOf(address(erc1271MockContract), schemeId), stealthMetaAddress - ); + registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); + assertEq(registry.stealthMetaAddressOf(registrant, schemeId), stealthMetaAddress); } } - function testFuzz_EmitsStealthMetaAddressSetEventsForTheSameRegistrantAtLeastTwiceWhenProvidedAValidErc1271Signature( - string memory name, + function testFuzz_UpdatesTheStealthMetaAddressForAnExistingRegistrantWhenProvidedAValidErc1271Signature( uint256 schemeId, bytes memory stealthMetaAddress, + bytes memory signature, uint256 numOfUpdates ) external { - numOfUpdates = bound(numOfUpdates, 1, 50); + numOfUpdates = bound(numOfUpdates, 2, 50); - (address alice, uint256 alicePk) = makeAddrAndKey(name); - - vm.prank(alice); - ERC1271MockContract erc1271MockContract = new ERC1271MockContract(); + MockERC1271Signer mockRegistrant = new MockERC1271Signer(); + mockRegistrant.setResponse__isValidSignature(true); for (uint256 nonce = 0; nonce < numOfUpdates; nonce++) { - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, nonce); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, sigUtils.getTypedDataHash(registrantInfo)); - bytes memory signature = abi.encodePacked(r, s, v); - - vm.expectEmit(true, true, true, true); - emit StealthMetaAddressSet(address(erc1271MockContract), schemeId, stealthMetaAddress); registry.registerKeysOnBehalf( - address(erc1271MockContract), schemeId, signature, stealthMetaAddress + address(mockRegistrant), schemeId, signature, stealthMetaAddress ); + assertEq(registry.stealthMetaAddressOf(address(mockRegistrant), schemeId), stealthMetaAddress); } } - function testFuzz_RevertIf_AnErc712SignatureFromAnAddressIsDifferentFromTheOneBeingRegistered( - string memory name, - address bob, + function testFuzz_RevertIf_TheDataIsErc712SignedByAnAddressOtherThanTheRegistrant( + string memory seed, + address registrant, uint256 schemeId, bytes memory stealthMetaAddress ) external { - ( /*address alice */ , uint256 alicePk) = makeAddrAndKey(name); - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, 0 /* nonce */ ); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); - - vm.expectRevert(); - registry.registerKeysOnBehalf(bob, schemeId, signature, stealthMetaAddress); + (address notRegistrant, uint256 notRegistrantPrivateKey) = makeAddrAndKey(seed); + vm.assume(notRegistrant != registrant); + _notVmOrConsole(registrant); + bytes memory signature = + _generateRegistrationSignature(notRegistrantPrivateKey, schemeId, stealthMetaAddress, 0); + + vm.expectRevert(bytes("")); + registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); } - function testFuzz_RevertIf_AnErc1271SignatureFromAnAddressIsDifferentFromTheOneBeingRegistered( - string memory name, - address bob, + function testFuzz_RevertIf_TheRegistrantErc712SignsOverTheWrongSchemeId( + string memory registrantSeed, uint256 schemeId, + uint256 wrongSchemeId, bytes memory stealthMetaAddress ) external { - ( /* address alice */ , uint256 alicePk) = makeAddrAndKey(name); - - vm.prank(bob); - ERC1271MockContract erc1271MockContract = new ERC1271MockContract(); - address registrant = address(erc1271MockContract); + vm.assume(schemeId != wrongSchemeId); + (address registrant, uint256 registrantPrivateKey) = makeAddrAndKey(registrantSeed); - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, 0 /* nonce */ ); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); + bytes memory signature = + _generateRegistrationSignature(registrantPrivateKey, wrongSchemeId, stealthMetaAddress, 0); - vm.expectRevert(ERC6538Registry__InvalidSignature.selector); + vm.expectRevert(bytes("")); registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); } - function testFuzz_RevertIf_ANewRegistrantProvidesAnErc712SignatureWithANonceOtherThanZero( - string memory name, + function testFuzz_RevertIf_TheRegistrantErc712SignsOverTheWrongStealthMetaAddress( + string memory registrantSeed, uint256 schemeId, bytes memory stealthMetaAddress, - uint256 nonce + bytes memory wrongStealthAMetaAddress ) external { - vm.assume(nonce != 0); - (address alice, uint256 alicePk) = makeAddrAndKey(name); - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, nonce); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); - vm.expectRevert(); - registry.registerKeysOnBehalf(alice, schemeId, signature, stealthMetaAddress); + vm.assume(keccak256(stealthMetaAddress) != keccak256(wrongStealthAMetaAddress)); + (address registrant, uint256 registrantPrivateKey) = makeAddrAndKey(registrantSeed); + + bytes memory signature = + _generateRegistrationSignature(registrantPrivateKey, schemeId, wrongStealthAMetaAddress, 0); + + vm.expectRevert(bytes("")); + registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); } - function testFuzz_RevertIf_ANewRegistrantProvidesAnErc1271SignatureWithANonceOtherThanZero( - string memory name, + function testFuzz_RevertIf_ANewRegistrantErc712SignsOverANonZeroNonce( + string memory registrantSeed, uint256 schemeId, bytes memory stealthMetaAddress, uint256 nonce ) external { vm.assume(nonce != 0); - (address alice, uint256 alicePk) = makeAddrAndKey(name); + (address registrant, uint256 registrantPrivateKey) = makeAddrAndKey(registrantSeed); + bytes memory signature = + _generateRegistrationSignature(registrantPrivateKey, schemeId, stealthMetaAddress, nonce); + + vm.expectRevert(bytes("")); + registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); + } - vm.prank(alice); - ERC1271MockContract erc1271MockContract = new ERC1271MockContract(); + function testFuzz_RevertIf_APreviouslyUsedErc712SignatureIsReplayed( + string memory registrantSeed, + uint256 schemeId, + bytes memory stealthMetaAddress + ) external { + (address registrant, uint256 registrantPrivateKey) = makeAddrAndKey(registrantSeed); + bytes memory signature = + _generateRegistrationSignature(registrantPrivateKey, schemeId, stealthMetaAddress, 0); - SigUtils.RegistrantInfo memory registrantInfo = - SigUtils.RegistrantInfo(schemeId, stealthMetaAddress, nonce); - bytes32 hash = sigUtils.getTypedDataHash(registrantInfo); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash); - bytes memory signature = abi.encodePacked(r, s, v); + registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); + + vm.expectRevert(bytes("")); + registry.registerKeysOnBehalf(registrant, schemeId, signature, stealthMetaAddress); + } + + function testFuzz_RevertIf_TheErc1271SignatureIsNotValid( + uint256 schemeId, + bytes memory stealthMetaAddress, + bytes memory signature + ) external { + MockERC1271Signer mockRegistrant = new MockERC1271Signer(); + mockRegistrant.setResponse__isValidSignature(false); vm.expectRevert(ERC6538Registry__InvalidSignature.selector); - registry.registerKeysOnBehalf( - address(erc1271MockContract), schemeId, signature, stealthMetaAddress - ); + registry.registerKeysOnBehalf(address(mockRegistrant), schemeId, signature, stealthMetaAddress); } - function testFuzz_RevertIf_AnEmptySignatureIsUsedToRegisterKeysForANewRegistrant( + function testFuzz_RevertIf_AnEmptySignatureIsSubmitted( address registrant, uint256 schemeId, bytes memory stealthMetaAddress ) external { - vm.expectRevert(); + _notVmOrConsole(registrant); + vm.expectRevert(bytes("")); registry.registerKeysOnBehalf(registrant, schemeId, "", stealthMetaAddress); } } contract IncrementNonce is ERC6538RegistryTest { - function testFuzz_IncrementsTheNonceOfTheCallerByOneNTimesEachTimeTheIncrementNonceFunctionIsCalledByTheCaller( - address registrant, - uint256 numOfCalls - ) external { + function testFuzz_IncrementsTheNonceOfTheCaller(address registrant, uint256 numOfCalls) external { numOfCalls = bound(numOfCalls, 2, 50); - uint256 nonce = registry.nonceOf(address(registrant)); for (uint256 i = 1; i < numOfCalls; i++) { - vm.startPrank(registrant); + vm.prank(registrant); registry.incrementNonce(); - assertEq(registry.nonceOf(registrant), nonce + i); - vm.stopPrank(); + + assertEq(registry.nonceOf(registrant), i); } } } contract Domain_Separator is ERC6538RegistryTest { - function testFuzz_ChecksTheDomainSeparatorOfTheErc6538RegistryContractAfterAChainSplitIsRecomputedWithTheNewChainId( - uint256 chainId - ) external { - chainId = bound(chainId, 1, 1_000_000); - vm.chainId(chainId); - assertEq( - registry.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address registryContract)" - ), - keccak256("ERC6538Registry"), - keccak256("1.0"), - chainId, - address(registry) - ) - ) - ); - } -} - -contract ERC1271MockContract { - address public owner; - - enum RecoverError { - NoError, - InvalidSignature, - InvalidSignatureLength, - InvalidSignatureS - } - - error ECDSAInvalidSignature(); - error ECDSAInvalidSignatureLength(uint256 length); - error ECDSAInvalidSignatureS(bytes32 s); - - constructor() { - owner = msg.sender; + function test_ReturnsTheValueComputedAtDeploymentIfChainIdRemainsUnchanged() external { + ERC6538RegistryHarness _registry = new ERC6538RegistryHarness(); + assertEq(_registry.DOMAIN_SEPARATOR(), _registry.exposed_INITIAL_DOMAIN_SEPARATOR()); } - function isValidSignature(bytes32 hash, bytes memory signature) - public - view - returns (bytes4 magicValue) - { - return recover(hash, signature) == owner ? this.isValidSignature.selector : bytes4(0); - } - - function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { - (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); - _throwError(error, errorArg); - return recovered; - } - - function tryRecover(bytes32 hash, bytes memory signature) - internal - pure - returns (address, RecoverError, bytes32) - { - if (signature.length == 65) { - bytes32 r; - bytes32 s; - uint8 v; - // ecrecover takes the signature parameters, and the only way to get them currently is to use - // assembly. - assembly ("memory-safe") { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - } - return tryRecover(hash, v, r, s); - } else { - return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); - } - } - - function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) - internal - pure - returns (address, RecoverError, bytes32) + function testFuzz_ReturnsARecomputedValueIfTheChainIdChanges(uint256 chainId1, uint256 chainId2) + external { - if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { - return (address(0), RecoverError.InvalidSignatureS, s); - } + chainId1 = bound(chainId1, 1, 2 ** 64 - 1); + chainId2 = bound(chainId2, 1, 2 ** 64 - 1); - // If the signature is valid (and not malleable), return the signer address - address signer = ecrecover(hash, v, r, s); - if (signer == address(0)) return (address(0), RecoverError.InvalidSignature, bytes32(0)); + vm.chainId(chainId1); + assertEq(registry.DOMAIN_SEPARATOR(), _computeDomainSeparator(chainId1, address(registry))); - return (signer, RecoverError.NoError, bytes32(0)); - } - - function _throwError(RecoverError error, bytes32 errorArg) private pure { - if (error == RecoverError.NoError) { - return; // no error: do nothing - } else if (error == RecoverError.InvalidSignature) { - revert ECDSAInvalidSignature(); - } else if (error == RecoverError.InvalidSignatureLength) { - revert ECDSAInvalidSignatureLength(uint256(errorArg)); - } else if (error == RecoverError.InvalidSignatureS) { - revert ECDSAInvalidSignatureS(errorArg); - } + vm.chainId(chainId2); + assertEq(registry.DOMAIN_SEPARATOR(), _computeDomainSeparator(chainId2, address(registry))); } } -contract SigUtils { - bytes32 internal DOMAIN_SEPARATOR; +contract MockERC1271Signer is IERC1271 { + bytes4 public constant MAGICVALUE = 0x1626ba7e; + bytes4 public response__isValidSignature; - constructor(bytes32 _DOMAIN_SEPARATOR) { - DOMAIN_SEPARATOR = _DOMAIN_SEPARATOR; - } - - struct RegistrantInfo { - uint256 schemeId; - bytes stealthMetaAddress; - uint256 nonce; - } - - // computes the hash - function getStructHash(RegistrantInfo memory _info) internal pure returns (bytes32) { - return keccak256( - abi.encode( - keccak256("Erc6538RegistryEntry(uint256 schemeId,bytes stealthMetaAddress,uint256 nonce)"), - _info.schemeId, - _info.stealthMetaAddress, - _info.nonce - ) - ); + function setResponse__isValidSignature(bool _nextResponse) external { + if (_nextResponse) { + // If the mock should signal the signature is valid, it should return the MAGICVALUE + response__isValidSignature = MAGICVALUE; + } else { + // If the mock should signal it is not valid, we'll return an arbitrary four bytes derived + // from the address where the mock happens to be deployed + response__isValidSignature = bytes4(keccak256(abi.encode(address(this)))); + } } - // computes the hash of the fully encoded EIP-712 message for the domain, which can be used to - // recover the signer - function getTypedDataHash(RegistrantInfo memory _info) public view returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, getStructHash(_info))); - } -} -/// @notice Interface of the ERC1271 standard signature validation method for contracts as defined -/// in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. - -interface IERC1271 { - /// @dev Should return whether the signature provided is valid for the provided data - /// @param hash Hash of the data to be signed - /// @param signature Signature byte array associated with _data - function isValidSignature(bytes32 hash, bytes memory signature) + function isValidSignature(bytes32, /* hash */ bytes memory /* signature */ ) external view - returns (bytes4 magicValue); + returns (bytes4 magicValue) + { + magicValue = response__isValidSignature; + } }