From 46f6816931048c6907c2427407dc705f2111f95c Mon Sep 17 00:00:00 2001 From: Seth Date: Sat, 10 Feb 2024 17:21:59 +0800 Subject: [PATCH 1/3] feat: multicall --- src/BaseDocumentStore.sol | 48 +++--- src/DocumentStore.sol | 4 - src/interfaces/IDocumentStoreBatchable.sol | 3 +- test/CommonTest.t.sol | 13 +- test/DocumentStore.t.sol | 192 +++++++++++---------- 5 files changed, 136 insertions(+), 124 deletions(-) diff --git a/src/BaseDocumentStore.sol b/src/BaseDocumentStore.sol index aaa644d..d062d5c 100644 --- a/src/BaseDocumentStore.sol +++ b/src/BaseDocumentStore.sol @@ -4,8 +4,8 @@ pragma solidity >=0.8.23 <0.9.0; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; +import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; -import "./interfaces/IDocumentStore.sol"; import "./interfaces/IDocumentStoreBatchable.sol"; import "./base/DocumentStoreAccessControl.sol"; @@ -15,8 +15,8 @@ import "./base/DocumentStoreAccessControl.sol"; */ abstract contract BaseDocumentStore is Initializable, + MulticallUpgradeable, IDocumentStoreBatchable, - IDocumentStore, DocumentStoreAccessControl { using MerkleProof for bytes32[]; @@ -53,15 +53,15 @@ abstract contract BaseDocumentStore is _issue(documentRoot); } - /** - * @notice Issues multiple documents - * @param documentRoots The hashes of the documents to issue - */ - function bulkIssue(bytes32[] memory documentRoots) external onlyRole(ISSUER_ROLE) { - for (uint256 i = 0; i < documentRoots.length; i++) { - _issue(documentRoots[i]); - } - } +// /** +// * @notice Issues multiple documents +// * @param documentRoots The hashes of the documents to issue +// */ +// function bulkIssue(bytes32[] memory documentRoots) external onlyRole(ISSUER_ROLE) { +// for (uint256 i = 0; i < documentRoots.length; i++) { +// _issue(documentRoots[i]); +// } +// } function revoke(bytes32 documentRoot, bytes32 document, bytes32[] memory proof) external onlyRole(REVOKER_ROLE) { _revoke(documentRoot, document, proof); @@ -75,19 +75,19 @@ abstract contract BaseDocumentStore is _revoke(documentRoot, documentRoot, new bytes32[](0)); } - /** - * @notice Revokes documents in bulk - * @param documentRoots The hashes of the documents to revoke - */ - function bulkRevoke( - bytes32[] memory documentRoots, - bytes32[] memory documents, - bytes32[][] memory proofs - ) external onlyRole(REVOKER_ROLE) { - for (uint256 i = 0; i < documentRoots.length; i++) { - _revoke(documentRoots[i], documents[i], proofs[i]); - } - } +// /** +// * @notice Revokes documents in bulk +// * @param documentRoots The hashes of the documents to revoke +// */ +// function bulkRevoke( +// bytes32[] memory documentRoots, +// bytes32[] memory documents, +// bytes32[][] memory proofs +// ) external onlyRole(REVOKER_ROLE) { +// for (uint256 i = 0; i < documentRoots.length; i++) { +// _revoke(documentRoots[i], documents[i], proofs[i]); +// } +// } function isIssued( bytes32 documentRoot, diff --git a/src/DocumentStore.sol b/src/DocumentStore.sol index 449ba41..f7c1482 100644 --- a/src/DocumentStore.sol +++ b/src/DocumentStore.sol @@ -2,11 +2,7 @@ pragma solidity >=0.8.23 <0.9.0; -import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; - import "./BaseDocumentStore.sol"; -import "./base/DocumentStoreAccessControl.sol"; -import "./interfaces/IDocumentStoreBatchable.sol"; /** * @title DocumentStore diff --git a/src/interfaces/IDocumentStoreBatchable.sol b/src/interfaces/IDocumentStoreBatchable.sol index 9dacfd8..1eb490f 100644 --- a/src/interfaces/IDocumentStoreBatchable.sol +++ b/src/interfaces/IDocumentStoreBatchable.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.8.23 <0.9.0; +import "./IDocumentStore.sol"; -interface IDocumentStoreBatchable { +interface IDocumentStoreBatchable is IDocumentStore { function revoke(bytes32 documentRoot, bytes32 document, bytes32[] memory proof) external; function isIssued(bytes32 documentRoot, bytes32 document, bytes32[] memory proof) external view returns (bool); diff --git a/test/CommonTest.t.sol b/test/CommonTest.t.sol index f8ce9f7..aeae736 100644 --- a/test/CommonTest.t.sol +++ b/test/CommonTest.t.sol @@ -25,14 +25,12 @@ abstract contract CommonTest is Test { } } -abstract contract DocumentStoreWithFakeDocuments_Base is CommonTest { +abstract contract BatchedDocuments_Initializer is CommonTest { bytes32 public docRoot; bytes32[] public documents = new bytes32[](3); bytes32[][] public proofs = new bytes32[][](3); - function setUp() public virtual override { - super.setUp(); - + constructor() { docRoot = 0x5f0ed7e331c430ce34bcb45e2ddbff2b56a0f5971a226eee85f7ed6cc85e8e27; documents = [ @@ -53,4 +51,11 @@ abstract contract DocumentStoreWithFakeDocuments_Base is CommonTest { ]; proofs.push([bytes32(0x3763f4f892fb4c2ff4d76c4b9d391985568f8940f93f71283a84ff73277fb81e)]); } + + function setUp() public virtual override { + super.setUp(); + + vm.prank(issuer); + documentStore.issue(docRoot); + } } diff --git a/test/DocumentStore.t.sol b/test/DocumentStore.t.sol index 059f8fd..774435b 100644 --- a/test/DocumentStore.t.sol +++ b/test/DocumentStore.t.sol @@ -7,6 +7,7 @@ import "@openzeppelin/contracts/access/IAccessControl.sol"; import "../src/DocumentStore.sol"; import "../src/interfaces/IDocumentStore.sol"; +import "../src/interfaces/IDocumentStoreBatchable.sol"; import "./CommonTest.t.sol"; contract DocumentStore_init_Test is CommonTest { @@ -109,19 +110,21 @@ contract DocumentStore_issue_Test is CommonTest { } } -contract DocumentStore_bulkIssue_Test is CommonTest { +contract DocumentStore_multicallIssue_Test is CommonTest { bytes32[] public docHashes; + bytes[] public bulkIssueData; + function setUp() public override { super.setUp(); - vm.startPrank(owner); - documentStore.grantRole(documentStore.ISSUER_ROLE(), issuer); - vm.stopPrank(); - docHashes = new bytes32[](2); docHashes[0] = "0x1234"; docHashes[1] = "0x5678"; + + bulkIssueData = new bytes[](2); + bulkIssueData[0] = abi.encodeCall(IDocumentStore.issue, (docHashes[0])); + bulkIssueData[1] = abi.encodeCall(IDocumentStore.issue, (docHashes[1])); } function testBulkIssueByIssuer() public { @@ -131,25 +134,12 @@ contract DocumentStore_bulkIssue_Test is CommonTest { emit IDocumentStore.DocumentIssued(docHashes[1]); vm.prank(issuer); - documentStore.bulkIssue(docHashes); + documentStore.multicall(bulkIssueData); assert(documentStore.isIssued(docHashes[0])); assert(documentStore.isIssued(docHashes[1])); } - function testBulkIssueByRevokerRevert() public { - vm.expectRevert( - abi.encodeWithSelector( - IAccessControl.AccessControlUnauthorizedAccount.selector, - revoker, - documentStore.ISSUER_ROLE() - ) - ); - - vm.prank(revoker); - documentStore.bulkIssue(docHashes); - } - function testBulkIssueByNonIssuerRevert() public { address notIssuer = vm.addr(69); @@ -162,27 +152,21 @@ contract DocumentStore_bulkIssue_Test is CommonTest { ); vm.prank(notIssuer); - documentStore.bulkIssue(docHashes); + documentStore.multicall(bulkIssueData); } function testBulkIssueWithDuplicatesRevert() public { docHashes[1] = docHashes[0]; + bulkIssueData[1] = abi.encodeCall(IDocumentStore.issue, (docHashes[0])); vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentExists.selector, bytes32(docHashes[1]))); vm.prank(issuer); - documentStore.bulkIssue(docHashes); + documentStore.multicall(bulkIssueData); } } -contract DocumentStore_isIssued_Test is DocumentStoreWithFakeDocuments_Base { - function setUp() public override { - super.setUp(); - - vm.prank(issuer); - documentStore.issue(docRoot); - } - +contract DocumentStore_isIssued_Test is BatchedDocuments_Initializer { function testIsRootIssuedWithRoot() public { assertTrue(documentStore.isIssued(docRoot)); } @@ -224,14 +208,7 @@ contract DocumentStore_isIssued_Test is DocumentStoreWithFakeDocuments_Base { } } -contract DocumentStore_revokeRoot_Test is DocumentStoreWithFakeDocuments_Base { - function setUp() public override { - super.setUp(); - - vm.prank(issuer); - documentStore.issue(docRoot); - } - +contract DocumentStore_revokeRoot_Test is BatchedDocuments_Initializer { function testRevokeRootByOwner() public { vm.expectEmit(true, true, false, true); emit IDocumentStore.DocumentRevoked(docRoot, docRoot); @@ -307,14 +284,7 @@ contract DocumentStore_revokeRoot_Test is DocumentStoreWithFakeDocuments_Base { } } -contract DocumentStore_revoke_Test is DocumentStoreWithFakeDocuments_Base { - function setUp() public override { - super.setUp(); - - vm.prank(issuer); - documentStore.issue(docRoot); - } - +contract DocumentStoreBatchable_revoke_Test is BatchedDocuments_Initializer { function testRevokeByOwner() public { vm.expectEmit(true, true, false, true); emit IDocumentStore.DocumentRevoked(docRoot, documents[0]); @@ -401,6 +371,18 @@ contract DocumentStore_revoke_Test is DocumentStoreWithFakeDocuments_Base { vm.stopPrank(); } + function testRevokeAlreadyRevokedRootRevert() public { + vm.startPrank(revoker); + + documentStore.revoke(docRoot); + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoot, documents[0])); + + documentStore.revoke(docRoot, documents[0], proofs[0]); + + vm.stopPrank(); + } + function testRevokeNonIssuedDocumentRevert(bytes32 nonIssuedRoot) public { vm.assume(nonIssuedRoot != docRoot && nonIssuedRoot != bytes32(0)); @@ -411,53 +393,43 @@ contract DocumentStore_revoke_Test is DocumentStoreWithFakeDocuments_Base { } } -contract DocumentStore_bulkRevoke_Test is DocumentStoreWithFakeDocuments_Base { +abstract contract DocumentStore_multicall_revoke_Base is BatchedDocuments_Initializer { bytes32[] public docRoots = new bytes32[](3); + bytes[] public bulkRevokeData; - function setUp() public override { - super.setUp(); - - docRoots[0] = docRoot; - docRoots[1] = docRoot; - docRoots[2] = docRoot; - - vm.prank(issuer); - documentStore.issue(docRoot); - } - - function testBulkRevokeByOwner() public { + function testMulticallRevokeByOwner() public { vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, documents[0]); + emit IDocumentStore.DocumentRevoked(docRoots[0], documents[0]); vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, documents[1]); + emit IDocumentStore.DocumentRevoked(docRoots[1], documents[1]); vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, documents[2]); + emit IDocumentStore.DocumentRevoked(docRoots[2], documents[2]); vm.prank(owner); - documentStore.bulkRevoke(docRoots, documents, proofs); + documentStore.multicall(bulkRevokeData); - assertTrue(documentStore.isRevoked(docRoot, documents[0], proofs[0])); - assertTrue(documentStore.isRevoked(docRoot, documents[1], proofs[1])); - assertTrue(documentStore.isRevoked(docRoot, documents[2], proofs[2])); + assertTrue(documentStore.isRevoked(docRoots[0], documents[0], proofs[0])); + assertTrue(documentStore.isRevoked(docRoots[1], documents[1], proofs[1])); + assertTrue(documentStore.isRevoked(docRoots[2], documents[2], proofs[2])); } - function testBulkRevokeByRevoker() public { + function testMulticallRevokeByRevoker() public { vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, documents[0]); + emit IDocumentStore.DocumentRevoked(docRoots[0], documents[0]); vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, documents[1]); + emit IDocumentStore.DocumentRevoked(docRoots[1], documents[1]); vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, documents[2]); + emit IDocumentStore.DocumentRevoked(docRoots[2], documents[2]); vm.prank(revoker); - documentStore.bulkRevoke(docRoots, documents, proofs); + documentStore.multicall(bulkRevokeData); - assertTrue(documentStore.isRevoked(docRoot, documents[0], proofs[0])); - assertTrue(documentStore.isRevoked(docRoot, documents[1], proofs[1])); - assertTrue(documentStore.isRevoked(docRoot, documents[2], proofs[2])); + assertTrue(documentStore.isRevoked(docRoots[0], documents[0], proofs[0])); + assertTrue(documentStore.isRevoked(docRoots[1], documents[1], proofs[1])); + assertTrue(documentStore.isRevoked(docRoots[2], documents[2], proofs[2])); } - function testBulkRevokeByIssuerRevert() public { + function testMulticallRevokeByIssuerRevert() public { vm.expectRevert( abi.encodeWithSelector( IAccessControl.AccessControlUnauthorizedAccount.selector, @@ -467,10 +439,10 @@ contract DocumentStore_bulkRevoke_Test is DocumentStoreWithFakeDocuments_Base { ); vm.prank(issuer); - documentStore.bulkRevoke(docRoots, documents, proofs); + documentStore.multicall(bulkRevokeData); } - function testBulkRevokeByNonRevokerRevert() public { + function testMulticallRevokeByNonRevokerRevert() public { address notRevoker = vm.addr(69); vm.expectRevert( @@ -482,29 +454,71 @@ contract DocumentStore_bulkRevoke_Test is DocumentStoreWithFakeDocuments_Base { ); vm.prank(notRevoker); - documentStore.bulkRevoke(docRoots, documents, proofs); + documentStore.multicall(bulkRevokeData); } - function testBulkRevokeWithDuplicatesRevert() public { + function testMulticallRevokeWithDuplicatesRevert() public { + // Make document0 and document1 with same data docRoots[1] = docRoots[0]; documents[1] = documents[0]; proofs[1] = proofs[0]; + bulkRevokeData[1] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoots[0], documents[0], proofs[0])); vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoots[1], documents[1])); vm.prank(revoker); - documentStore.bulkRevoke(docRoots, documents, proofs); + documentStore.multicall(bulkRevokeData); } } -contract DocumentStore_isRevoked_Test is DocumentStoreWithFakeDocuments_Base { +contract DocumentStoreBatchable_multicall_revoke_Test is DocumentStore_multicall_revoke_Base { function setUp() public override { super.setUp(); - vm.startPrank(owner); - documentStore.issue(docRoot); - documentStore.revoke(docRoot, documents[0], proofs[0]); + docRoots[0] = docRoot; + docRoots[1] = docRoot; + docRoots[2] = docRoot; + + bulkRevokeData = new bytes[](3); + bulkRevokeData[0] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot, documents[0], proofs[0])); + bulkRevokeData[1] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot, documents[1], proofs[1])); + bulkRevokeData[2] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot, documents[2], proofs[2])); + } +} + +contract DocumentStore_multicall_revoke_Test is DocumentStore_multicall_revoke_Base { + function setUp() public override { + super.setUp(); + + // Set up the document fixtures to be independent documents + docRoots[0] = documents[0]; + docRoots[1] = documents[1]; + docRoots[2] = documents[2]; + + // We want the documents to be independent, thus no need proofs + proofs[0] = new bytes32[](0); + proofs[1] = new bytes32[](0); + proofs[2] = new bytes32[](0); + + vm.startPrank(issuer); + documentStore.issue(docRoots[0]); + documentStore.issue(docRoots[1]); + documentStore.issue(docRoots[2]); vm.stopPrank(); + + bulkRevokeData = new bytes[](3); + bulkRevokeData[0] = abi.encodeCall(IDocumentStore.revoke, (documents[0])); + bulkRevokeData[1] = abi.encodeCall(IDocumentStore.revoke, (documents[1])); + bulkRevokeData[2] = abi.encodeCall(IDocumentStore.revoke, (documents[2])); + } +} + +contract DocumentStore_isRevoked_Test is BatchedDocuments_Initializer { + function setUp() public override { + super.setUp(); + + vm.prank(revoker); + documentStore.revoke(docRoot, documents[0], proofs[0]); } function testIsRevokedWithRevokedDocument() public { @@ -551,14 +565,12 @@ contract DocumentStore_isRevoked_Test is DocumentStoreWithFakeDocuments_Base { } } -contract DocumentStore_isRootRevoked is DocumentStoreWithFakeDocuments_Base { +contract DocumentStore_isRootRevoked is BatchedDocuments_Initializer { function setUp() public override { super.setUp(); - vm.startPrank(owner); - documentStore.issue(docRoot); + vm.prank(revoker); documentStore.revoke(docRoot); - vm.stopPrank(); } function testIsRootRevokedWithRevokedRoot() public { @@ -589,14 +601,12 @@ contract DocumentStore_isRootRevoked is DocumentStoreWithFakeDocuments_Base { } } -contract DocumentStore_isActive_Test is DocumentStoreWithFakeDocuments_Base { +contract DocumentStore_isActive_Test is BatchedDocuments_Initializer { function setUp() public override { super.setUp(); - vm.startPrank(owner); - documentStore.issue(docRoot); + vm.prank(revoker); documentStore.revoke(docRoot, documents[0], proofs[0]); - vm.stopPrank(); } function testIsActiveWithActiveDocument() public { From 6ccb3de4b1f151112d1a2475697aef6a06eeb0d2 Mon Sep 17 00:00:00 2001 From: Seth Date: Sat, 10 Feb 2024 18:48:13 +0800 Subject: [PATCH 2/3] test: add multicall tests --- src/BaseDocumentStore.sol | 24 -- test/CommonTest.t.sol | 206 ++++++++++++-- test/DocumentStore.t.sol | 380 +++---------------------- test/DocumentStoreBatchable.t.sol | 240 ++++++++++++++++ test/fixtures/DocumentStoreFixture.sol | 58 ++++ 5 files changed, 529 insertions(+), 379 deletions(-) create mode 100644 test/DocumentStoreBatchable.t.sol create mode 100644 test/fixtures/DocumentStoreFixture.sol diff --git a/src/BaseDocumentStore.sol b/src/BaseDocumentStore.sol index d062d5c..b6f0af5 100644 --- a/src/BaseDocumentStore.sol +++ b/src/BaseDocumentStore.sol @@ -53,16 +53,6 @@ abstract contract BaseDocumentStore is _issue(documentRoot); } -// /** -// * @notice Issues multiple documents -// * @param documentRoots The hashes of the documents to issue -// */ -// function bulkIssue(bytes32[] memory documentRoots) external onlyRole(ISSUER_ROLE) { -// for (uint256 i = 0; i < documentRoots.length; i++) { -// _issue(documentRoots[i]); -// } -// } - function revoke(bytes32 documentRoot, bytes32 document, bytes32[] memory proof) external onlyRole(REVOKER_ROLE) { _revoke(documentRoot, document, proof); } @@ -75,20 +65,6 @@ abstract contract BaseDocumentStore is _revoke(documentRoot, documentRoot, new bytes32[](0)); } -// /** -// * @notice Revokes documents in bulk -// * @param documentRoots The hashes of the documents to revoke -// */ -// function bulkRevoke( -// bytes32[] memory documentRoots, -// bytes32[] memory documents, -// bytes32[][] memory proofs -// ) external onlyRole(REVOKER_ROLE) { -// for (uint256 i = 0; i < documentRoots.length; i++) { -// _revoke(documentRoots[i], documents[i], proofs[i]); -// } -// } - function isIssued( bytes32 documentRoot, bytes32 document, diff --git a/test/CommonTest.t.sol b/test/CommonTest.t.sol index aeae736..1d4e7dc 100644 --- a/test/CommonTest.t.sol +++ b/test/CommonTest.t.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.23 <0.9.0; import "forge-std/Test.sol"; import "../src/DocumentStore.sol"; +import "./fixtures/DocumentStoreFixture.sol"; abstract contract CommonTest is Test { string public storeName = "DocumentStore Test"; @@ -25,37 +26,202 @@ abstract contract CommonTest is Test { } } -abstract contract BatchedDocuments_Initializer is CommonTest { +abstract contract DocumentStore_Initializer is CommonTest { + bytes32[] public documents; + + DocumentStoreFixture private _fixture; + + function setUp() public virtual override { + super.setUp(); + + _fixture = new DocumentStoreFixture(); + + documents = _fixture.documents(); + + bytes[] memory issueData = new bytes[](3); + issueData[0] = abi.encodeCall(documentStore.issue, (documents[0])); + issueData[1] = abi.encodeCall(documentStore.issue, (documents[1])); + issueData[2] = abi.encodeCall(documentStore.issue, (documents[2])); + + vm.prank(issuer); + documentStore.multicall(issueData); + } +} + +abstract contract DocumentStoreBatchable_Initializer is CommonTest { bytes32 public docRoot; bytes32[] public documents = new bytes32[](3); bytes32[][] public proofs = new bytes32[][](3); - constructor() { - docRoot = 0x5f0ed7e331c430ce34bcb45e2ddbff2b56a0f5971a226eee85f7ed6cc85e8e27; + DocumentStoreBatchableFixture private _fixture; + + function setUp() public virtual override { + super.setUp(); - documents = [ - bytes32(0x795bb6abe4c5bb81e397821324d44bf7a94785587d0c88c621f57268c8aef4cb), - bytes32(0x9bc394ef702b639adb913242a472e883f4834b4f38ed38f046bec8fcc1104fa3), - bytes32(0x4aac698f1a67c980d0a52901fe4805775cc31beae66fb33bbb9dd89d30de81bd) - ]; + _fixture = new DocumentStoreBatchableFixture(); - proofs = [ - [ - bytes32(0x9bc394ef702b639adb913242a472e883f4834b4f38ed38f046bec8fcc1104fa3), - bytes32(0x4aac698f1a67c980d0a52901fe4805775cc31beae66fb33bbb9dd89d30de81bd) - ], - [ - bytes32(0x795bb6abe4c5bb81e397821324d44bf7a94785587d0c88c621f57268c8aef4cb), - bytes32(0x4aac698f1a67c980d0a52901fe4805775cc31beae66fb33bbb9dd89d30de81bd) - ] - ]; - proofs.push([bytes32(0x3763f4f892fb4c2ff4d76c4b9d391985568f8940f93f71283a84ff73277fb81e)]); + docRoot = _fixture.docRoot(); + + documents = _fixture.documents(); + + proofs = _fixture.proofs(); + + vm.prank(issuer); + documentStore.issue(docRoot); + } +} + +abstract contract DocumentStore_multicall_revoke_Base is CommonTest { + bytes[] public bulkRevokeData; + + function docRoots() public view virtual returns (bytes32[] memory); + + function documents() public view virtual returns (bytes32[] memory); + + function proofs() public view virtual returns (bytes32[][] memory); + + function testMulticallRevokeByOwner() public { + vm.expectEmit(true, true, false, true); + emit IDocumentStore.DocumentRevoked(docRoots()[0], documents()[0]); + vm.expectEmit(true, true, false, true); + emit IDocumentStore.DocumentRevoked(docRoots()[1], documents()[1]); + vm.expectEmit(true, true, false, true); + emit IDocumentStore.DocumentRevoked(docRoots()[2], documents()[2]); + + vm.prank(owner); + documentStore.multicall(bulkRevokeData); + + assertTrue(documentStore.isRevoked(docRoots()[0], documents()[0], proofs()[0])); + assertTrue(documentStore.isRevoked(docRoots()[1], documents()[1], proofs()[1])); + assertTrue(documentStore.isRevoked(docRoots()[2], documents()[2], proofs()[2])); + } + + function testMulticallRevokeByRevoker() public { + vm.expectEmit(true, true, false, true); + emit IDocumentStore.DocumentRevoked(docRoots()[0], documents()[0]); + vm.expectEmit(true, true, false, true); + emit IDocumentStore.DocumentRevoked(docRoots()[1], documents()[1]); + vm.expectEmit(true, true, false, true); + emit IDocumentStore.DocumentRevoked(docRoots()[2], documents()[2]); + + vm.prank(revoker); + documentStore.multicall(bulkRevokeData); + + assertTrue(documentStore.isRevoked(docRoots()[0], documents()[0], proofs()[0])); + assertTrue(documentStore.isRevoked(docRoots()[1], documents()[1], proofs()[1])); + assertTrue(documentStore.isRevoked(docRoots()[2], documents()[2], proofs()[2])); + } + + function testMulticallRevokeByIssuerRevert() public { + vm.expectRevert( + abi.encodeWithSelector( + IAccessControl.AccessControlUnauthorizedAccount.selector, + issuer, + documentStore.REVOKER_ROLE() + ) + ); + + vm.prank(issuer); + documentStore.multicall(bulkRevokeData); + } + + function testMulticallRevokeByNonRevokerRevert() public { + address notRevoker = vm.addr(69); + + vm.expectRevert( + abi.encodeWithSelector( + IAccessControl.AccessControlUnauthorizedAccount.selector, + notRevoker, + documentStore.REVOKER_ROLE() + ) + ); + + vm.prank(notRevoker); + documentStore.multicall(bulkRevokeData); + } + + function testMulticallRevokeWithDuplicatesRevert() public { + // Make document1 same as document0 + bulkRevokeData[1] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoots()[0], documents()[0], proofs()[0])); + + // It should revert that document0 is already inactive + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoots()[0], documents()[0])); + + vm.prank(revoker); + documentStore.multicall(bulkRevokeData); + } +} + +abstract contract DocumentStoreBatchable_multicall_revoke_Initializer is DocumentStore_multicall_revoke_Base { + DocumentStoreBatchableFixture private _fixture; + + function docRoot() public view virtual returns (bytes32) { + return _fixture.docRoot(); + } + + function docRoots() public view virtual override returns (bytes32[] memory) { + bytes32[] memory roots = new bytes32[](3); + roots[0] = _fixture.docRoot(); + roots[1] = _fixture.docRoot(); + roots[2] = _fixture.docRoot(); + return roots; + } + + function documents() public view virtual override returns (bytes32[] memory) { + return _fixture.documents(); + } + + function proofs() public view virtual override returns (bytes32[][] memory) { + return _fixture.proofs(); } function setUp() public virtual override { super.setUp(); + _fixture = new DocumentStoreBatchableFixture(); + + vm.startPrank(issuer); + documentStore.issue(docRoot()); + vm.stopPrank(); + } +} + +abstract contract DocumentStore_multicall_revoke_Initializer is DocumentStore_multicall_revoke_Base { + DocumentStoreFixture private _fixture; + + function docRoots() public view virtual override returns (bytes32[] memory) { + // Set up the document fixtures to be independent documents + bytes32[] memory roots = new bytes32[](3); + roots[0] = _fixture.documents()[0]; + roots[1] = _fixture.documents()[1]; + roots[2] = _fixture.documents()[2]; + return roots; + } + + function documents() public view virtual override returns (bytes32[] memory) { + return _fixture.documents(); + } + + function proofs() public view virtual override returns (bytes32[][] memory) { + // We want the documents to be independent, thus no need proofs + bytes32[][] memory _proofs = new bytes32[][](3); + _proofs[0] = new bytes32[](0); + _proofs[1] = new bytes32[](0); + _proofs[2] = new bytes32[](0); + return _proofs; + } + + function setUp() public virtual override { + super.setUp(); + + _fixture = new DocumentStoreFixture(); + + bytes[] memory issueData = new bytes[](3); + issueData[0] = abi.encodeCall(documentStore.issue, (documents()[0])); + issueData[1] = abi.encodeCall(documentStore.issue, (documents()[1])); + issueData[2] = abi.encodeCall(documentStore.issue, (documents()[2])); + vm.prank(issuer); - documentStore.issue(docRoot); + documentStore.multicall(issueData); } } diff --git a/test/DocumentStore.t.sol b/test/DocumentStore.t.sol index 774435b..cc8edc3 100644 --- a/test/DocumentStore.t.sol +++ b/test/DocumentStore.t.sol @@ -110,7 +110,7 @@ contract DocumentStore_issue_Test is CommonTest { } } -contract DocumentStore_multicallIssue_Test is CommonTest { +contract DocumentStore_multicall_Issue_Test is CommonTest { bytes32[] public docHashes; bytes[] public bulkIssueData; @@ -166,7 +166,7 @@ contract DocumentStore_multicallIssue_Test is CommonTest { } } -contract DocumentStore_isIssued_Test is BatchedDocuments_Initializer { +contract DocumentStore_isIssued_Test is DocumentStoreBatchable_Initializer { function testIsRootIssuedWithRoot() public { assertTrue(documentStore.isIssued(docRoot)); } @@ -208,101 +208,33 @@ contract DocumentStore_isIssued_Test is BatchedDocuments_Initializer { } } -contract DocumentStore_revokeRoot_Test is BatchedDocuments_Initializer { - function testRevokeRootByOwner() public { - vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, docRoot); - - vm.prank(owner); - documentStore.revoke(docRoot); - - assertTrue(documentStore.isRevoked(docRoot)); - } - - function testRevokeRootByRevoker() public { - vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, docRoot); - - vm.prank(revoker); - documentStore.revoke(docRoot); - - assertTrue(documentStore.isRevoked(docRoot)); - } - - function testRevokeRootByIssuerRevert() public { - vm.expectRevert( - abi.encodeWithSelector( - IAccessControl.AccessControlUnauthorizedAccount.selector, - issuer, - documentStore.REVOKER_ROLE() - ) - ); - - vm.prank(issuer); - documentStore.revoke(docRoot); - } - - function testRevokeRootByNonRevokerRevert() public { - address notRevoker = vm.addr(69); - - vm.expectRevert( - abi.encodeWithSelector( - IAccessControl.AccessControlUnauthorizedAccount.selector, - notRevoker, - documentStore.REVOKER_ROLE() - ) - ); - - vm.prank(notRevoker); - documentStore.revoke(docRoot); - } - - function testRevokeRootWithZeroRoot() public { - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - - vm.prank(revoker); - documentStore.revoke(0x0); - } - - function testRevokeRootAlreadyRevokedRevert() public { - vm.startPrank(revoker); - documentStore.revoke(docRoot); - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoot, docRoot)); - - documentStore.revoke(docRoot); - vm.stopPrank(); - } - - function testRevokeRootNonIssuedRootRevert(bytes32 nonIssuedRoot) public { - vm.assume(nonIssuedRoot != docRoot && nonIssuedRoot != bytes32(0)); +contract DocumentStore_revoke_Test is DocumentStore_Initializer { + bytes32 internal targetDoc; - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentNotIssued.selector, nonIssuedRoot, nonIssuedRoot)); + function setUp() public override { + super.setUp(); - vm.prank(revoker); - documentStore.revoke(nonIssuedRoot); + targetDoc = documents[0]; } -} -contract DocumentStoreBatchable_revoke_Test is BatchedDocuments_Initializer { function testRevokeByOwner() public { vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, documents[0]); + emit IDocumentStore.DocumentRevoked(targetDoc, targetDoc); vm.prank(owner); - documentStore.revoke(docRoot, documents[0], proofs[0]); + documentStore.revoke(targetDoc); - assertTrue(documentStore.isRevoked(docRoot, documents[0], proofs[0])); + assertTrue(documentStore.isRevoked(targetDoc)); } function testRevokeByRevoker() public { vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoot, documents[0]); + emit IDocumentStore.DocumentRevoked(targetDoc, targetDoc); vm.prank(revoker); - documentStore.revoke(docRoot, documents[0], proofs[0]); + documentStore.revoke(targetDoc); - assertTrue(documentStore.isRevoked(docRoot, documents[0], proofs[0])); + assertTrue(documentStore.isRevoked(targetDoc)); } function testRevokeByIssuerRevert() public { @@ -315,7 +247,7 @@ contract DocumentStoreBatchable_revoke_Test is BatchedDocuments_Initializer { ); vm.prank(issuer); - documentStore.revoke(docRoot, documents[0], proofs[0]); + documentStore.revoke(targetDoc); } function testRevokeByNonRevokerRevert() public { @@ -330,61 +262,28 @@ contract DocumentStoreBatchable_revoke_Test is BatchedDocuments_Initializer { ); vm.prank(notRevoker); - documentStore.revoke(docRoot, documents[0], proofs[0]); - } - - function testRevokeWithInvalidProofRevert() public { - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); - - vm.prank(revoker); - documentStore.revoke(docRoot, documents[0], proofs[1]); - } - - function testRevokeWithEmptyProofRevert() public { - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); - - vm.prank(revoker); - documentStore.revoke(docRoot, documents[0], new bytes32[](0)); + documentStore.revoke(targetDoc); } - function testRevokeWithZeroDocument() public { - vm.startPrank(revoker); - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - documentStore.revoke(0x0, documents[0], proofs[0]); - + function testRevokeWithZeroRoot() public { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - documentStore.revoke(docRoot, 0x0, proofs[0]); - vm.stopPrank(); + vm.prank(revoker); + documentStore.revoke(0x0); } function testRevokeAlreadyRevokedRevert() public { vm.startPrank(revoker); + documentStore.revoke(targetDoc); - documentStore.revoke(docRoot, documents[0], proofs[0]); - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoot, documents[0])); - - documentStore.revoke(docRoot, documents[0], proofs[0]); + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, targetDoc, targetDoc)); + documentStore.revoke(targetDoc); vm.stopPrank(); } - function testRevokeAlreadyRevokedRootRevert() public { - vm.startPrank(revoker); - - documentStore.revoke(docRoot); - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoot, documents[0])); - - documentStore.revoke(docRoot, documents[0], proofs[0]); - - vm.stopPrank(); - } - - function testRevokeNonIssuedDocumentRevert(bytes32 nonIssuedRoot) public { - vm.assume(nonIssuedRoot != docRoot && nonIssuedRoot != bytes32(0)); + function testRevokeNotIssuedRootRevert() public { + bytes32 nonIssuedRoot = "0x1234"; vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentNotIssued.selector, nonIssuedRoot, nonIssuedRoot)); @@ -393,267 +292,78 @@ contract DocumentStoreBatchable_revoke_Test is BatchedDocuments_Initializer { } } -abstract contract DocumentStore_multicall_revoke_Base is BatchedDocuments_Initializer { - bytes32[] public docRoots = new bytes32[](3); - bytes[] public bulkRevokeData; - - function testMulticallRevokeByOwner() public { - vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoots[0], documents[0]); - vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoots[1], documents[1]); - vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoots[2], documents[2]); - - vm.prank(owner); - documentStore.multicall(bulkRevokeData); - - assertTrue(documentStore.isRevoked(docRoots[0], documents[0], proofs[0])); - assertTrue(documentStore.isRevoked(docRoots[1], documents[1], proofs[1])); - assertTrue(documentStore.isRevoked(docRoots[2], documents[2], proofs[2])); - } - - function testMulticallRevokeByRevoker() public { - vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoots[0], documents[0]); - vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoots[1], documents[1]); - vm.expectEmit(true, true, false, true); - emit IDocumentStore.DocumentRevoked(docRoots[2], documents[2]); - - vm.prank(revoker); - documentStore.multicall(bulkRevokeData); - - assertTrue(documentStore.isRevoked(docRoots[0], documents[0], proofs[0])); - assertTrue(documentStore.isRevoked(docRoots[1], documents[1], proofs[1])); - assertTrue(documentStore.isRevoked(docRoots[2], documents[2], proofs[2])); - } - - function testMulticallRevokeByIssuerRevert() public { - vm.expectRevert( - abi.encodeWithSelector( - IAccessControl.AccessControlUnauthorizedAccount.selector, - issuer, - documentStore.REVOKER_ROLE() - ) - ); - - vm.prank(issuer); - documentStore.multicall(bulkRevokeData); - } - - function testMulticallRevokeByNonRevokerRevert() public { - address notRevoker = vm.addr(69); - - vm.expectRevert( - abi.encodeWithSelector( - IAccessControl.AccessControlUnauthorizedAccount.selector, - notRevoker, - documentStore.REVOKER_ROLE() - ) - ); - - vm.prank(notRevoker); - documentStore.multicall(bulkRevokeData); - } - - function testMulticallRevokeWithDuplicatesRevert() public { - // Make document0 and document1 with same data - docRoots[1] = docRoots[0]; - documents[1] = documents[0]; - proofs[1] = proofs[0]; - bulkRevokeData[1] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoots[0], documents[0], proofs[0])); - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoots[1], documents[1])); - - vm.prank(revoker); - documentStore.multicall(bulkRevokeData); - } -} - -contract DocumentStoreBatchable_multicall_revoke_Test is DocumentStore_multicall_revoke_Base { +contract DocumentStore_multicall_revoke_Test is DocumentStore_multicall_revoke_Initializer { function setUp() public override { super.setUp(); - docRoots[0] = docRoot; - docRoots[1] = docRoot; - docRoots[2] = docRoot; - bulkRevokeData = new bytes[](3); - bulkRevokeData[0] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot, documents[0], proofs[0])); - bulkRevokeData[1] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot, documents[1], proofs[1])); - bulkRevokeData[2] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot, documents[2], proofs[2])); + bulkRevokeData[0] = abi.encodeCall(IDocumentStore.revoke, (documents()[0])); + bulkRevokeData[1] = abi.encodeCall(IDocumentStore.revoke, (documents()[1])); + bulkRevokeData[2] = abi.encodeCall(IDocumentStore.revoke, (documents()[2])); } } -contract DocumentStore_multicall_revoke_Test is DocumentStore_multicall_revoke_Base { - function setUp() public override { - super.setUp(); - - // Set up the document fixtures to be independent documents - docRoots[0] = documents[0]; - docRoots[1] = documents[1]; - docRoots[2] = documents[2]; - - // We want the documents to be independent, thus no need proofs - proofs[0] = new bytes32[](0); - proofs[1] = new bytes32[](0); - proofs[2] = new bytes32[](0); +contract DocumentStore_isRevoked_Test is DocumentStore_Initializer { + bytes32 public targetDocument; - vm.startPrank(issuer); - documentStore.issue(docRoots[0]); - documentStore.issue(docRoots[1]); - documentStore.issue(docRoots[2]); - vm.stopPrank(); - - bulkRevokeData = new bytes[](3); - bulkRevokeData[0] = abi.encodeCall(IDocumentStore.revoke, (documents[0])); - bulkRevokeData[1] = abi.encodeCall(IDocumentStore.revoke, (documents[1])); - bulkRevokeData[2] = abi.encodeCall(IDocumentStore.revoke, (documents[2])); - } -} - -contract DocumentStore_isRevoked_Test is BatchedDocuments_Initializer { function setUp() public override { super.setUp(); + targetDocument = documents[0]; + vm.prank(revoker); - documentStore.revoke(docRoot, documents[0], proofs[0]); + documentStore.revoke(targetDocument); } function testIsRevokedWithRevokedDocument() public { - assertTrue(documentStore.isRevoked(docRoot, documents[0], proofs[0])); - } - - function testIsRevokedWithRevokedRoot() public { - vm.prank(revoker); - documentStore.revoke(docRoot); - - assertTrue(documentStore.isRevoked(docRoot, documents[1], proofs[1])); + assertTrue(documentStore.isRevoked(targetDocument)); } function testIsRevokedWithNotRevokedDocument() public { - assertFalse(documentStore.isRevoked(docRoot, documents[1], proofs[1])); - } - - function testIsRevokedWithInvalidProofRevert() public { - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); - - documentStore.isRevoked(docRoot, documents[0], proofs[1]); - } - - function testIsRevokedWithEmptyProofRevert() public { - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); - - documentStore.isRevoked(docRoot, documents[0], new bytes32[](0)); + assertFalse(documentStore.isRevoked(documents[2])); } function testIsRevokedWithZeroDocumentRevert() public { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - documentStore.isRevoked(docRoot, 0x0, proofs[0]); - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - documentStore.isRevoked(0x0, documents[0], proofs[0]); - } - - function testIsRevokedWithNotIssuedDocumentRevert() public { - bytes32 notIssuedDoc = "0x1234"; - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, notIssuedDoc)); - - documentStore.isRevoked(docRoot, notIssuedDoc, proofs[0]); - } -} - -contract DocumentStore_isRootRevoked is BatchedDocuments_Initializer { - function setUp() public override { - super.setUp(); - - vm.prank(revoker); - documentStore.revoke(docRoot); - } - - function testIsRootRevokedWithRevokedRoot() public { - assertTrue(documentStore.isRevoked(docRoot)); - } - - function testIsRootRevokedWithNotRevokedRoot(bytes32 notRevokedRoot) public { - vm.assume(notRevokedRoot != docRoot && notRevokedRoot != bytes32(0)); - - vm.prank(issuer); - documentStore.issue(notRevokedRoot); - - assertFalse(documentStore.isRevoked(notRevokedRoot)); - } - - function testIsRootRevokedWithZeroRootRevert() public { - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); documentStore.isRevoked(0x0); } - function testIsRootRevokedWithNotIssuedRootRevert(bytes32 notIssuedRoot) public { - vm.assume(notIssuedRoot != docRoot && notIssuedRoot != bytes32(0)); + function testIsRevokedWithNotIssuedDocumentRevert() public { + bytes32 notIssuedRoot = "0x1234"; vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentNotIssued.selector, notIssuedRoot, notIssuedRoot)); - assertFalse(documentStore.isRevoked(notIssuedRoot)); + documentStore.isRevoked(notIssuedRoot); } } -contract DocumentStore_isActive_Test is BatchedDocuments_Initializer { +contract DocumentStore_isActive_Test is DocumentStore_Initializer { function setUp() public override { super.setUp(); vm.prank(revoker); - documentStore.revoke(docRoot, documents[0], proofs[0]); + documentStore.revoke(documents[0]); } function testIsActiveWithActiveDocument() public { - assertTrue(documentStore.isActive(docRoot, documents[1], proofs[1])); + assertTrue(documentStore.isActive(documents[1])); } function testIsActiveWithRevokedDocument() public { - assertFalse(documentStore.isActive(docRoot, documents[0], proofs[0])); - } - - function testIsActiveWithInvalidProofRevert() public { - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); - - documentStore.isActive(docRoot, documents[0], proofs[1]); - } - - function testIsActiveWithEmptyProofRevert() public { - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); - - documentStore.isActive(docRoot, documents[0], new bytes32[](0)); + assertFalse(documentStore.isActive(documents[0])); } function testIsActiveWithZeroDocumentRevert() public { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - documentStore.isActive(docRoot, 0x0, proofs[0]); - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - documentStore.isActive(0x0, documents[0], proofs[0]); + documentStore.isActive(0x0); } - function testIsActiveWithNotIssuedDocumentRevert(bytes32 notIssuedDoc) public { - vm.assume(notIssuedDoc != docRoot && notIssuedDoc != bytes32(0)); + function testIsActiveWithNotIssuedDocumentRevert() public { + bytes32 notIssuedDoc = "0x1234"; vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentNotIssued.selector, notIssuedDoc, notIssuedDoc)); - documentStore.isActive(notIssuedDoc, notIssuedDoc, new bytes32[](0)); - } - - function testIsActiveWithNotIssuedRootRevert() public { - bytes32 notIssuedRoot = 0xb841229d504c5c9bcb8132078db8c4a483825ad811078144c6f9aec84213d798; - bytes32 notIssuedDoc = 0xd56c26db0fde817dcd82269d0f9a3f50ea256ee0c870e43c3ec2ebdd655e3f37; - - bytes32[] memory proofs = new bytes32[](1); - proofs[0] = 0x9800b3feae3c44fe4263f6cbb2d8dd529c26c3a1c3ca7208a30cfa5efbc362e7; - - vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentNotIssued.selector, notIssuedRoot, notIssuedDoc)); - - documentStore.isActive(notIssuedRoot, notIssuedDoc, proofs); + documentStore.isActive(notIssuedDoc); } } diff --git a/test/DocumentStoreBatchable.t.sol b/test/DocumentStoreBatchable.t.sol new file mode 100644 index 0000000..229ac80 --- /dev/null +++ b/test/DocumentStoreBatchable.t.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.8.23 <0.9.0; + +import "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import "@openzeppelin/contracts/access/IAccessControl.sol"; + +import "../src/DocumentStore.sol"; +import "../src/interfaces/IDocumentStore.sol"; +import "../src/interfaces/IDocumentStoreBatchable.sol"; +import "./CommonTest.t.sol"; + +contract DocumentStoreBatchable_revoke_Test is DocumentStoreBatchable_Initializer { + function testRevokeByOwner() public { + vm.expectEmit(true, true, false, true); + emit IDocumentStore.DocumentRevoked(docRoot, documents[0]); + + vm.prank(owner); + documentStore.revoke(docRoot, documents[0], proofs[0]); + + assertTrue(documentStore.isRevoked(docRoot, documents[0], proofs[0])); + } + + function testRevokeByRevoker() public { + vm.expectEmit(true, true, false, true); + emit IDocumentStore.DocumentRevoked(docRoot, documents[0]); + + vm.prank(revoker); + documentStore.revoke(docRoot, documents[0], proofs[0]); + + assertTrue(documentStore.isRevoked(docRoot, documents[0], proofs[0])); + } + + function testRevokeByIssuerRevert() public { + vm.expectRevert( + abi.encodeWithSelector( + IAccessControl.AccessControlUnauthorizedAccount.selector, + issuer, + documentStore.REVOKER_ROLE() + ) + ); + + vm.prank(issuer); + documentStore.revoke(docRoot, documents[0], proofs[0]); + } + + function testRevokeByNonRevokerRevert() public { + address notRevoker = vm.addr(69); + + vm.expectRevert( + abi.encodeWithSelector( + IAccessControl.AccessControlUnauthorizedAccount.selector, + notRevoker, + documentStore.REVOKER_ROLE() + ) + ); + + vm.prank(notRevoker); + documentStore.revoke(docRoot, documents[0], proofs[0]); + } + + function testRevokeWithInvalidProofRevert() public { + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); + + vm.prank(revoker); + documentStore.revoke(docRoot, documents[0], proofs[1]); + } + + function testRevokeWithEmptyProofRevert() public { + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); + + vm.prank(revoker); + documentStore.revoke(docRoot, documents[0], new bytes32[](0)); + } + + function testRevokeWithZeroDocument() public { + vm.startPrank(revoker); + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); + documentStore.revoke(0x0, documents[0], proofs[0]); + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); + documentStore.revoke(docRoot, 0x0, proofs[0]); + + vm.stopPrank(); + } + + function testRevokeAlreadyRevokedRevert() public { + vm.startPrank(revoker); + + documentStore.revoke(docRoot, documents[0], proofs[0]); + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoot, documents[0])); + + documentStore.revoke(docRoot, documents[0], proofs[0]); + + vm.stopPrank(); + } + + function testRevokeAlreadyRevokedRootRevert() public { + vm.startPrank(revoker); + + documentStore.revoke(docRoot); + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoot, documents[0])); + + documentStore.revoke(docRoot, documents[0], proofs[0]); + + vm.stopPrank(); + } + + function testRevokeNotIssuedDocumentRevert() public { + bytes32 nonIssuedRoot = "0x1234"; + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentNotIssued.selector, nonIssuedRoot, nonIssuedRoot)); + + vm.prank(revoker); + documentStore.revoke(nonIssuedRoot); + } +} + +contract DocumentStoreBatchable_multicall_revoke_Test is DocumentStoreBatchable_multicall_revoke_Initializer { + function setUp() public override { + super.setUp(); + + bulkRevokeData = new bytes[](3); + bulkRevokeData[0] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot(), documents()[0], proofs()[0])); + bulkRevokeData[1] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot(), documents()[1], proofs()[1])); + bulkRevokeData[2] = abi.encodeCall(IDocumentStoreBatchable.revoke, (docRoot(), documents()[2], proofs()[2])); + } +} + +contract DocumentStoreBatchable_isRevoked_Test is DocumentStoreBatchable_Initializer { + function setUp() public override { + super.setUp(); + + vm.prank(revoker); + documentStore.revoke(docRoot, documents[0], proofs[0]); + } + + function testIsRevokedWithRevokedDocument() public { + assertTrue(documentStore.isRevoked(docRoot, documents[0], proofs[0])); + } + + function testIsRevokedWithRevokedRoot() public { + vm.prank(revoker); + documentStore.revoke(docRoot); + + assertTrue(documentStore.isRevoked(docRoot, documents[1], proofs[1])); + } + + function testIsRevokedWithNotRevokedDocument() public { + assertFalse(documentStore.isRevoked(docRoot, documents[1], proofs[1])); + } + + function testIsRevokedWithInvalidProofRevert() public { + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); + + documentStore.isRevoked(docRoot, documents[0], proofs[1]); + } + + function testIsRevokedWithEmptyProofRevert() public { + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); + + documentStore.isRevoked(docRoot, documents[0], new bytes32[](0)); + } + + function testIsRevokedWithZeroDocumentRevert() public { + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); + documentStore.isRevoked(docRoot, 0x0, proofs[0]); + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); + documentStore.isRevoked(0x0, documents[0], proofs[0]); + } + + function testIsRevokedWithNotIssuedDocumentRevert() public { + bytes32 notIssuedDoc = "0x1234"; + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, notIssuedDoc)); + + documentStore.isRevoked(docRoot, notIssuedDoc, proofs[0]); + } +} + +contract DocumentStoreBatchable_isActive_Test is DocumentStoreBatchable_Initializer { + function setUp() public override { + super.setUp(); + + vm.prank(revoker); + documentStore.revoke(docRoot, documents[0], proofs[0]); + } + + function testIsActiveWithActiveDocument() public { + assertTrue(documentStore.isActive(docRoot, documents[1], proofs[1])); + } + + function testIsActiveWithRevokedDocument() public { + assertFalse(documentStore.isActive(docRoot, documents[0], proofs[0])); + } + + function testIsActiveWithInvalidProofRevert() public { + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); + + documentStore.isActive(docRoot, documents[0], proofs[1]); + } + + function testIsActiveWithEmptyProofRevert() public { + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, docRoot, documents[0])); + + documentStore.isActive(docRoot, documents[0], new bytes32[](0)); + } + + function testIsActiveWithZeroDocumentRevert() public { + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); + documentStore.isActive(docRoot, 0x0, proofs[0]); + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); + documentStore.isActive(0x0, documents[0], proofs[0]); + } + + function testIsActiveWithNotIssuedDocumentRevert(bytes32 notIssuedDoc) public { + vm.assume(notIssuedDoc != docRoot && notIssuedDoc != bytes32(0)); + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentNotIssued.selector, notIssuedDoc, notIssuedDoc)); + + documentStore.isActive(notIssuedDoc, notIssuedDoc, new bytes32[](0)); + } + + function testIsActiveWithNotIssuedRootRevert() public { + bytes32 notIssuedRoot = 0xb841229d504c5c9bcb8132078db8c4a483825ad811078144c6f9aec84213d798; + bytes32 notIssuedDoc = 0xd56c26db0fde817dcd82269d0f9a3f50ea256ee0c870e43c3ec2ebdd655e3f37; + + bytes32[] memory proofs = new bytes32[](1); + proofs[0] = 0x9800b3feae3c44fe4263f6cbb2d8dd529c26c3a1c3ca7208a30cfa5efbc362e7; + + vm.expectRevert(abi.encodeWithSelector(IDocumentStore.DocumentNotIssued.selector, notIssuedRoot, notIssuedDoc)); + + documentStore.isActive(notIssuedRoot, notIssuedDoc, proofs); + } +} diff --git a/test/fixtures/DocumentStoreFixture.sol b/test/fixtures/DocumentStoreFixture.sol new file mode 100644 index 0000000..d385f30 --- /dev/null +++ b/test/fixtures/DocumentStoreFixture.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.8.23 <0.9.0; + +contract DocumentStoreFixture { + bytes32[] internal _documents; + + constructor() { + _documents = [ + bytes32(0x795bb6abe4c5bb81e397821324d44bf7a94785587d0c88c621f57268c8aef4cb), + bytes32(0x9bc394ef702b639adb913242a472e883f4834b4f38ed38f046bec8fcc1104fa3), + bytes32(0x4aac698f1a67c980d0a52901fe4805775cc31beae66fb33bbb9dd89d30de81bd) + ]; + } + + function documents() public view returns (bytes32[] memory) { + return _documents; + } +} + +contract DocumentStoreBatchableFixture { + bytes32 internal _docRoot; + bytes32[] internal _documents = new bytes32[](3); + bytes32[][] internal _proofs = new bytes32[][](3); + + constructor() { + _docRoot = 0x5f0ed7e331c430ce34bcb45e2ddbff2b56a0f5971a226eee85f7ed6cc85e8e27; + + _documents = [ + bytes32(0x795bb6abe4c5bb81e397821324d44bf7a94785587d0c88c621f57268c8aef4cb), + bytes32(0x9bc394ef702b639adb913242a472e883f4834b4f38ed38f046bec8fcc1104fa3), + bytes32(0x4aac698f1a67c980d0a52901fe4805775cc31beae66fb33bbb9dd89d30de81bd) + ]; + + _proofs = [ + [ + bytes32(0x9bc394ef702b639adb913242a472e883f4834b4f38ed38f046bec8fcc1104fa3), + bytes32(0x4aac698f1a67c980d0a52901fe4805775cc31beae66fb33bbb9dd89d30de81bd) + ], + [ + bytes32(0x795bb6abe4c5bb81e397821324d44bf7a94785587d0c88c621f57268c8aef4cb), + bytes32(0x4aac698f1a67c980d0a52901fe4805775cc31beae66fb33bbb9dd89d30de81bd) + ] + ]; + _proofs.push([bytes32(0x3763f4f892fb4c2ff4d76c4b9d391985568f8940f93f71283a84ff73277fb81e)]); + } + + function docRoot() public view returns (bytes32) { + return _docRoot; + } + + function documents() public view returns (bytes32[] memory) { + return _documents; + } + + function proofs() public view returns (bytes32[][] memory) { + return _proofs; + } +} From 2be27777af6f3fccd00ace2a84aec5da5dcbe522 Mon Sep 17 00:00:00 2001 From: Seth Date: Sun, 11 Feb 2024 01:15:46 +0800 Subject: [PATCH 3/3] test: add erc165 tests (cherry picked from commit d8e96e45bd9367079ec207c5855343f6cfb848f1) --- test/DocumentStore.t.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/DocumentStore.t.sol b/test/DocumentStore.t.sol index cc8edc3..ad97053 100644 --- a/test/DocumentStore.t.sol +++ b/test/DocumentStore.t.sol @@ -367,3 +367,11 @@ contract DocumentStore_isActive_Test is DocumentStore_Initializer { documentStore.isActive(notIssuedDoc); } } + +contract DocumentStore_supportsInterface_Test is CommonTest { + function testSupportsInterface() public { + assertTrue(documentStore.supportsInterface(type(IDocumentStore).interfaceId)); + assertTrue(documentStore.supportsInterface(type(IDocumentStoreBatchable).interfaceId)); + assertTrue(documentStore.supportsInterface(type(IAccessControl).interfaceId)); + } +}