From 79550a93f3ecf43256fab4ed1d8ccc63bc2b76bc Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 7 Feb 2024 16:43:36 +0800 Subject: [PATCH 1/5] refactor: streamline issue and revoke function names --- src/DocumentStore.sol | 8 +++---- test/DocumentStore.t.sol | 46 ++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/DocumentStore.sol b/src/DocumentStore.sol index 6ebf41b..2b2b97a 100644 --- a/src/DocumentStore.sol +++ b/src/DocumentStore.sol @@ -40,7 +40,7 @@ contract DocumentStore is DocumentStoreAccessControl, BaseDocumentStore { * @param documentRoot The hash of the document to issue */ function issue(bytes32 documentRoot) public onlyRole(ISSUER_ROLE) { - if (isRootIssued(documentRoot)) { + if (isIssued(documentRoot)) { revert DocumentExists(documentRoot); } @@ -63,7 +63,7 @@ contract DocumentStore is DocumentStoreAccessControl, BaseDocumentStore { * @notice Revokes a document * @param documentRoot The hash of the document to revoke */ - function revokeRoot(bytes32 documentRoot) public onlyRole(REVOKER_ROLE) { + function revoke(bytes32 documentRoot) public onlyRole(REVOKER_ROLE) { revoke(documentRoot, documentRoot, new bytes32[](0)); } @@ -101,7 +101,7 @@ contract DocumentStore is DocumentStoreAccessControl, BaseDocumentStore { return _isIssued(documentRoot); } - function isRootIssued(bytes32 documentRoot) public view returns (bool) { + function isIssued(bytes32 documentRoot) public view returns (bool) { return isIssued(documentRoot, documentRoot, new bytes32[](0)); } @@ -132,7 +132,7 @@ contract DocumentStore is DocumentStoreAccessControl, BaseDocumentStore { * @param documentRoot The hash of the document to check * @return A boolean indicating whether the document has been revoked */ - function isRootRevoked(bytes32 documentRoot) public view returns (bool) { + function isRevoked(bytes32 documentRoot) public view returns (bool) { return isRevoked(documentRoot, documentRoot, new bytes32[](0)); } diff --git a/test/DocumentStore.t.sol b/test/DocumentStore.t.sol index 21d2271..3fbd938 100644 --- a/test/DocumentStore.t.sol +++ b/test/DocumentStore.t.sol @@ -65,7 +65,7 @@ contract DocumentStore_issue_Test is CommonTest { vm.prank(owner); documentStore.issue(docHash); - assert(documentStore.isRootIssued(docHash)); + assert(documentStore.isIssued(docHash)); } function testIssueByIssuer() public { @@ -76,7 +76,7 @@ contract DocumentStore_issue_Test is CommonTest { vm.prank(issuer); documentStore.issue(docHash); - assert(documentStore.isRootIssued(docHash)); + assert(documentStore.isIssued(docHash)); } function testIssueByRevokerRevert() public { @@ -149,8 +149,8 @@ contract DocumentStore_bulkIssue_Test is CommonTest { vm.prank(issuer); documentStore.bulkIssue(docHashes); - assert(documentStore.isRootIssued(docHashes[0])); - assert(documentStore.isRootIssued(docHashes[1])); + assert(documentStore.isIssued(docHashes[0])); + assert(documentStore.isIssued(docHashes[1])); } function testBulkIssueByRevokerRevert() public { @@ -230,13 +230,13 @@ contract DocumentStore_isIssued_Test is DocumentStoreWithFakeDocuments_Base { } function testIsRootIssuedWithRoot() public { - assertTrue(documentStore.isRootIssued(docRoot)); + assertTrue(documentStore.isIssued(docRoot)); } function testIsRootIssuedWithZeroRoot() public { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - documentStore.isRootIssued(0x0); + documentStore.isIssued(0x0); } function testIsIssuedWithRoot() public { @@ -283,9 +283,9 @@ contract DocumentStore_revokeRoot_Test is DocumentStoreWithFakeDocuments_Base { emit IDocumentStore.DocumentRevoked(docRoot, docRoot); vm.prank(owner); - documentStore.revokeRoot(docRoot); + documentStore.revoke(docRoot); - assertTrue(documentStore.isRootRevoked(docRoot)); + assertTrue(documentStore.isRevoked(docRoot)); } function testRevokeRootByRevoker() public { @@ -293,9 +293,9 @@ contract DocumentStore_revokeRoot_Test is DocumentStoreWithFakeDocuments_Base { emit IDocumentStore.DocumentRevoked(docRoot, docRoot); vm.prank(revoker); - documentStore.revokeRoot(docRoot); + documentStore.revoke(docRoot); - assertTrue(documentStore.isRootRevoked(docRoot)); + assertTrue(documentStore.isRevoked(docRoot)); } function testRevokeRootByIssuerRevert() public { @@ -308,7 +308,7 @@ contract DocumentStore_revokeRoot_Test is DocumentStoreWithFakeDocuments_Base { ); vm.prank(issuer); - documentStore.revokeRoot(docRoot); + documentStore.revoke(docRoot); } function testRevokeRootByNonRevokerRevert() public { @@ -323,23 +323,23 @@ contract DocumentStore_revokeRoot_Test is DocumentStoreWithFakeDocuments_Base { ); vm.prank(notRevoker); - documentStore.revokeRoot(docRoot); + documentStore.revoke(docRoot); } function testRevokeRootWithZeroRoot() public { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); vm.prank(revoker); - documentStore.revokeRoot(0x0); + documentStore.revoke(0x0); } function testRevokeRootAlreadyRevokedRevert() public { vm.startPrank(revoker); - documentStore.revokeRoot(docRoot); + documentStore.revoke(docRoot); vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InactiveDocument.selector, docRoot, docRoot)); - documentStore.revokeRoot(docRoot); + documentStore.revoke(docRoot); vm.stopPrank(); } @@ -349,7 +349,7 @@ contract DocumentStore_revokeRoot_Test is DocumentStoreWithFakeDocuments_Base { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, nonIssuedRoot, nonIssuedRoot)); vm.prank(revoker); - documentStore.revokeRoot(nonIssuedRoot); + documentStore.revoke(nonIssuedRoot); } } @@ -453,7 +453,7 @@ contract DocumentStore_revoke_Test is DocumentStoreWithFakeDocuments_Base { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, nonIssuedRoot, nonIssuedRoot)); vm.prank(revoker); - documentStore.revokeRoot(nonIssuedRoot); + documentStore.revoke(nonIssuedRoot); } } @@ -559,7 +559,7 @@ contract DocumentStore_isRevoked_Test is DocumentStoreWithFakeDocuments_Base { function testIsRevokedWithRevokedRoot() public { vm.prank(revoker); - documentStore.revokeRoot(docRoot); + documentStore.revoke(docRoot); assertTrue(documentStore.isRevoked(docRoot, documents[1], proofs[1])); } @@ -603,12 +603,12 @@ contract DocumentStore_isRootRevoked is DocumentStoreWithFakeDocuments_Base { vm.startPrank(owner); documentStore.issue(docRoot); - documentStore.revokeRoot(docRoot); + documentStore.revoke(docRoot); vm.stopPrank(); } function testIsRootRevokedWithRevokedRoot() public { - assertTrue(documentStore.isRootRevoked(docRoot)); + assertTrue(documentStore.isRevoked(docRoot)); } function testIsRootRevokedWithNotRevokedRoot() public { @@ -617,13 +617,13 @@ contract DocumentStore_isRootRevoked is DocumentStoreWithFakeDocuments_Base { vm.prank(issuer); documentStore.issue(notRevokedRoot); - assertFalse(documentStore.isRootRevoked(notRevokedRoot)); + assertFalse(documentStore.isRevoked(notRevokedRoot)); } function testIsRootRevokedWithZeroRootRevert() public { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.ZeroDocument.selector)); - documentStore.isRootRevoked(0x0); + documentStore.isRevoked(0x0); } function testIsRootRevokedWithNotIssuedRootRevert() public { @@ -631,7 +631,7 @@ contract DocumentStore_isRootRevoked is DocumentStoreWithFakeDocuments_Base { vm.expectRevert(abi.encodeWithSelector(IDocumentStore.InvalidDocument.selector, notIssuedRoot, notIssuedRoot)); - assertFalse(documentStore.isRootRevoked(notIssuedRoot)); + assertFalse(documentStore.isRevoked(notIssuedRoot)); } } From 87f3f330fa8ad57dad9b9a9467896062b98c87c9 Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 7 Feb 2024 15:08:52 +0800 Subject: [PATCH 2/5] refactor: remove legacy functions (cherry picked from commit b3fb48b70e2f6920a890c2e0f1bd0e57fa9b5f63) --- src/BaseDocumentStore.sol | 41 +-------------------------------------- src/DocumentStore.sol | 10 +++------- 2 files changed, 4 insertions(+), 47 deletions(-) diff --git a/src/BaseDocumentStore.sol b/src/BaseDocumentStore.sol index 6dbde38..07b8c1e 100644 --- a/src/BaseDocumentStore.sol +++ b/src/BaseDocumentStore.sol @@ -10,7 +10,7 @@ import {IDocumentStore} from "./interfaces/IDocumentStore.sol"; * @title BaseDocumentStore * @notice A base contract for storing and revoking documents */ -contract BaseDocumentStore is Initializable, IDocumentStore { +abstract contract BaseDocumentStore is Initializable, IDocumentStore { /** * @notice The name of the contract */ @@ -40,16 +40,6 @@ contract BaseDocumentStore is Initializable, IDocumentStore { */ function _issue(bytes32 document) internal { documentIssued[document] = block.number; - // emit DocumentIssued(document); - } - - /** - * @notice Gets the block number at which a document was issued - * @param document The hash of the issued document - * @return The block number at which the document was issued - */ - function getIssuedBlock(bytes32 document) public view onlyIssued(document) returns (uint256) { - return documentIssued[document]; } /** @@ -61,16 +51,6 @@ contract BaseDocumentStore is Initializable, IDocumentStore { return (documentIssued[document] != 0); } - /** - * @notice Checks if a document was issued before a specific block number (inclusive) - * @param document The hash of the document to check - * @param blockNumber The block number to check against - * @return A boolean indicating whether the document was issued before the specified block number - */ - function isIssuedBefore(bytes32 document, uint256 blockNumber) public view returns (bool) { - return documentIssued[document] != 0 && documentIssued[document] <= blockNumber; - } - /** * @notice Revokes a document * @param document The hash of the document to revoke @@ -87,23 +67,4 @@ contract BaseDocumentStore is Initializable, IDocumentStore { function _isRevoked(bytes32 document) internal view returns (bool) { return documentRevoked[document] != 0; } - - /** - * @notice Checks if a document was revoked before a specific block number (inclusive) - * @param document The hash of the document to check - * @param blockNumber The block number to check against - * @return A boolean indicating whether the document was revoked before the specified block number - */ - function isRevokedBefore(bytes32 document, uint256 blockNumber) public view returns (bool) { - return documentRevoked[document] <= blockNumber && documentRevoked[document] != 0; - } - - /** - * @dev Checks if a document has been issued - * @param document The hash of the document to check - */ - modifier onlyIssued(bytes32 document) { - require(_isIssued(document), "Error: Only issued document hashes can be revoked"); - _; - } } diff --git a/src/DocumentStore.sol b/src/DocumentStore.sol index 2b2b97a..3df23da 100644 --- a/src/DocumentStore.sol +++ b/src/DocumentStore.sol @@ -113,14 +113,10 @@ contract DocumentStore is DocumentStoreAccessControl, BaseDocumentStore { if (!isIssued(documentRoot, document, proof)) { revert InvalidDocument(documentRoot, document); } - return _isRevokedInternal(documentRoot, document, proof); + return _isRevoked(documentRoot, document, proof); } - function _isRevokedInternal( - bytes32 documentRoot, - bytes32 document, - bytes32[] memory proof - ) internal view returns (bool) { + function _isRevoked(bytes32 documentRoot, bytes32 document, bytes32[] memory proof) internal view returns (bool) { if (documentRoot == document && proof.length == 0) { return _isRevoked(document); } @@ -140,7 +136,7 @@ contract DocumentStore is DocumentStoreAccessControl, BaseDocumentStore { if (!isIssued(documentRoot, document, proof)) { revert InvalidDocument(documentRoot, document); } - return !_isRevokedInternal(documentRoot, document, proof); + return !_isRevoked(documentRoot, document, proof); } modifier onlyValidDocument( From 5258e1fcb2d06daced549000ee0b4da1cb02dcbf Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 7 Feb 2024 21:26:59 +0800 Subject: [PATCH 3/5] refactor: clean up imports (cherry picked from commit c0f4d4daf124ad6247901370208441ad9233766d) --- src/BaseDocumentStore.sol | 1 - src/DocumentStore.sol | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/BaseDocumentStore.sol b/src/BaseDocumentStore.sol index 07b8c1e..d986150 100644 --- a/src/BaseDocumentStore.sol +++ b/src/BaseDocumentStore.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; -import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {IDocumentStore} from "./interfaces/IDocumentStore.sol"; diff --git a/src/DocumentStore.sol b/src/DocumentStore.sol index 3df23da..2af96e4 100644 --- a/src/DocumentStore.sol +++ b/src/DocumentStore.sol @@ -2,10 +2,7 @@ pragma solidity ^0.8.0; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; - import "./BaseDocumentStore.sol"; import "./base/DocumentStoreAccessControl.sol"; From 33d6933a2eadfc7def9f2afd3779765d9f074163 Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 7 Feb 2024 21:29:42 +0800 Subject: [PATCH 4/5] refactor: zero address owner custom error (cherry picked from commit 240b496deeacd58a3ee2e36de00478fcfd741465) --- src/base/DocumentStoreAccessControl.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/base/DocumentStoreAccessControl.sol b/src/base/DocumentStoreAccessControl.sol index 3fa372f..4c79f66 100644 --- a/src/base/DocumentStoreAccessControl.sol +++ b/src/base/DocumentStoreAccessControl.sol @@ -9,6 +9,8 @@ import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol" * @notice Base contract for managing access control roles for a DocumentStore */ contract DocumentStoreAccessControl is AccessControlUpgradeable { + error ZeroOwner(); + bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE"); bytes32 public constant REVOKER_ROLE = keccak256("REVOKER_ROLE"); @@ -17,7 +19,9 @@ contract DocumentStoreAccessControl is AccessControlUpgradeable { * @param owner The owner of the contract */ function __DocumentStoreAccessControl_init(address owner) internal onlyInitializing { - require(owner != address(0), "Owner is zero"); + if (owner == address(0)) { + revert ZeroOwner(); + } _grantRole(DEFAULT_ADMIN_ROLE, owner); _grantRole(ISSUER_ROLE, owner); _grantRole(REVOKER_ROLE, owner); From 6faa523eb222f3727626d8c5ba132723d662e32c Mon Sep 17 00:00:00 2001 From: Seth Date: Wed, 7 Feb 2024 23:37:09 +0800 Subject: [PATCH 5/5] chore: remove legacy files (cherry picked from commit eb3472f8b4eaf0b58b51fbc17e65e09a29a890f1) --- jest/setup.ts | 6 - jest/teardown.ts | 6 - jest/utils.ts | 12 - scripts/generateHashes.js | 22 -- scripts/postTypechain.js | 50 --- src/DocumentStoreCreator.sol | 32 -- src/DocumentStoreWithRevokeReasons.sol | 62 ---- test/DocumentStore.js | 467 ------------------------- test/DocumentStoreCreator.js | 37 -- 9 files changed, 694 deletions(-) delete mode 100644 jest/setup.ts delete mode 100644 jest/teardown.ts delete mode 100644 jest/utils.ts delete mode 100644 scripts/generateHashes.js delete mode 100644 scripts/postTypechain.js delete mode 100644 src/DocumentStoreCreator.sol delete mode 100644 src/DocumentStoreWithRevokeReasons.sol delete mode 100644 test/DocumentStore.js delete mode 100644 test/DocumentStoreCreator.js diff --git a/jest/setup.ts b/jest/setup.ts deleted file mode 100644 index bd6b383..0000000 --- a/jest/setup.ts +++ /dev/null @@ -1,6 +0,0 @@ -// setup.js -import { setupBlockchain } from "./utils"; - -module.exports = async () => { - await setupBlockchain(); -}; diff --git a/jest/teardown.ts b/jest/teardown.ts deleted file mode 100644 index 1b47e0d..0000000 --- a/jest/teardown.ts +++ /dev/null @@ -1,6 +0,0 @@ -// teardown.js -import { teardownBlockchain } from "./utils"; - -module.exports = async () => { - await teardownBlockchain(); -}; diff --git a/jest/utils.ts b/jest/utils.ts deleted file mode 100644 index 64b2cc2..0000000 --- a/jest/utils.ts +++ /dev/null @@ -1,12 +0,0 @@ -const ganache = require("ganache-cli"); - -let server: any; - -export async function setupBlockchain() { - server = ganache.server(); - server.listen(8545, () => {}); -} - -export async function teardownBlockchain() { - await server.close(); -} diff --git a/scripts/generateHashes.js b/scripts/generateHashes.js deleted file mode 100644 index 1575291..0000000 --- a/scripts/generateHashes.js +++ /dev/null @@ -1,22 +0,0 @@ -const defaultStartingHash = "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6432"; - -const generateHashes = (number, startingHash = defaultStartingHash) => { - let currentHash = BigInt(startingHash); - const hashes = []; - for (let i = 0; i < number; i += 1) { - currentHash += BigInt(1); - hashes.push(`0x${currentHash.toString(16)}`); - } - - return hashes; -}; - -const formatHashes = (hashes) => { - const hashesString = `[${hashes.join(", ")}]`; - return hashesString; -}; - -module.exports = { - generateHashes, - formatHashes, -}; diff --git a/scripts/postTypechain.js b/scripts/postTypechain.js deleted file mode 100644 index da5474a..0000000 --- a/scripts/postTypechain.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Post script to rename the default exported factory names by Typechain. - * Renamed factory names are only used for distribution and should not be used internally. - */ -const fs = require("fs"); -const readline = require("readline"); - -const typechainExportsFile = "src/contracts/index.ts"; -const typechainDistExportsFile = "src/contracts/index.dist.ts"; -const suffix = "__factory"; -const exportsArr = []; - -const readInterface = readline.createInterface({ - input: fs.createReadStream(typechainExportsFile), -}); - -const re = /^export\s*(type)?\s*{\s*(\w+)\s*(?:as.+)?}\s*from\s*"(.*)";$/m; -readInterface.on("line", (line) => { - const matches = line.match(re); - if (!matches) return; - const [, type, modName, path] = matches; - let asName; - if (modName.indexOf(suffix) !== -1) { - const len = modName.length - suffix.length; - asName = `${modName.substring(0, len)}Factory`; - } - exportsArr.push({ - name: modName, - type: type === "type", - path, - asName, - }); -}); - -readInterface.on("close", () => { - const headers = [ - "/* Autogenerated file. Do not edit manually. */", - "/* Do not import from this file. This is only for distribution. */", - "/* tslint:disable */", - "/* eslint-disable */", - ]; - const statements = exportsArr.map( - ({ name, type, asName, path }) => - `export${type ? " type" : ""} { ${name} ${asName ? `as ${asName}` : ""}} from "${path}";` - ); - if (!statements || !statements.length) return; - const data = [...headers, ...statements].join("\n"); - fs.writeFileSync(typechainDistExportsFile, data); - console.log(`Renamed Typechain generated exports and saved as ${typechainDistExportsFile}.`); -}); diff --git a/src/DocumentStoreCreator.sol b/src/DocumentStoreCreator.sol deleted file mode 100644 index b00825b..0000000 --- a/src/DocumentStoreCreator.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -pragma solidity ^0.8.0; - -import "./DocumentStore.sol"; - -/** - * @title DocumentStoreCreator - * @dev Naming this factory contract as DocumentStoreCreator so that typechain can name the factory of this - * contract as DocumentStoreCreatorFactory and it does not collide with the automatically generated - * DocumentStoreFactory automatically generated by typechain - */ -contract DocumentStoreCreator { - /** - * @notice Emitted when a new DocumentStore instance is deployed - * @param instance The address of the newly deployed DocumentStore instance - * @param creator The address of the creator of the new DocumentStore instance - */ - event DocumentStoreDeployed(address indexed instance, address indexed creator); - - /** - * @notice Deploys a new DocumentStore instance with the given name - * @param name The name of the new DocumentStore instance - * @return The address of the newly deployed DocumentStore instance - */ - function deploy(string memory name) public returns (address) { - // solhint-disable-next-line mark-callable-contracts - DocumentStore instance = new DocumentStore(name, msg.sender); - emit DocumentStoreDeployed(address(instance), msg.sender); - return address(instance); - } -} diff --git a/src/DocumentStoreWithRevokeReasons.sol b/src/DocumentStoreWithRevokeReasons.sol deleted file mode 100644 index 798ac8f..0000000 --- a/src/DocumentStoreWithRevokeReasons.sol +++ /dev/null @@ -1,62 +0,0 @@ -//// SPDX-License-Identifier: Apache-2.0 -// -//pragma solidity ^0.8.0; -// -//import "./DocumentStore.sol"; -// -///** -// * @title DocumentStoreWithRevokeReasons -// * @notice A contract for storing and revoking documents with access control and reasons for revocation -// */ -//contract DocumentStoreWithRevokeReasons is DocumentStore { -// /** -// * @notice A mapping of the document hash to the block number that was issued -// */ -// mapping(bytes32 => uint256) public revokeReason; -// -// /** -// * @notice Emitted when a document is revoked with a reason -// * @param document The hash of the revoked document -// * @param reason The reason for revocation -// */ -// event DocumentRevokedWithReason(bytes32 indexed document, uint256 reason); -// -// /** -// * @notice Initialises the contract with a name and owner -// * @param _name The name of the contract -// * @param owner The owner of the contract -// */ -// constructor(string memory _name, address owner) DocumentStore(_name, owner) {} -// -// /** -// * @notice Revokes a document with a reason -// * @param document The hash of the document to revoke -// * @param reason The reason for revocation -// * @return A boolean indicating whether the revocation was successful -// */ -// function revoke(bytes32 document, uint256 reason) -// public -// onlyRole(REVOKER_ROLE) -// onlyNotRevoked(document) -// returns (bool) -// { -// revoke(document); -// revokeReason[document] = reason; -// emit DocumentRevokedWithReason(document, reason); -// -// return true; -// } -// -// /** -// * @notice Revokes documents in bulk with a reason -// * @param documents The hashes of the documents to revoke -// * @param reason The reason for revocation -// */ -// function bulkRevoke(bytes32[] memory documents, uint256 reason) public { -// for (uint256 i = 0; i < documents.length; i++) { -// revoke(documents[i]); -// revokeReason[documents[i]] = reason; -// emit DocumentRevokedWithReason(documents[i], reason); -// } -// } -//} diff --git a/test/DocumentStore.js b/test/DocumentStore.js deleted file mode 100644 index 1453216..0000000 --- a/test/DocumentStore.js +++ /dev/null @@ -1,467 +0,0 @@ -const { expect, assert } = require("chai").use(require("chai-as-promised")); -const { ethers } = require("hardhat"); -const { get } = require("lodash"); -const config = require("../config.js"); - -describe("DocumentStore", async () => { - let Accounts; - let DocumentStore; - let DocumentStoreInstance; - - const adminRole = ethers.constants.HashZero; - const issuerRole = ethers.utils.id("ISSUER_ROLE"); - const revokerRole = ethers.utils.id("REVOKER_ROLE"); - - beforeEach("", async () => { - Accounts = await ethers.getSigners(); - DocumentStore = await ethers.getContractFactory("DocumentStore"); - DocumentStoreInstance = await DocumentStore.connect(Accounts[0]).deploy(config.INSTITUTE_NAME, Accounts[0].address); - await DocumentStoreInstance.deployed(); - }); - - describe("initializer", () => { - it("should have correct name", async () => { - const name = await DocumentStoreInstance.name(); - expect(name).to.be.equal(config.INSTITUTE_NAME, "Name of institute does not match"); - }); - }); - - describe("Access Control", () => { - describe("Initialisation", () => { - it("should revert if owner is zero address", async () => { - const tx = DocumentStore.connect(Accounts[0]).deploy(config.INSTITUTE_NAME, ethers.constants.AddressZero); - - await expect(tx).to.be.revertedWith("Owner is zero"); - }); - - describe("Owner Default Roles", () => { - it("should have default admin role", async () => { - const hasRole = await DocumentStoreInstance.hasRole(adminRole, Accounts[0].address); - - expect(hasRole).to.be.true; - }); - - it("should have issuer role", async () => { - const hasRole = await DocumentStoreInstance.hasRole(issuerRole, Accounts[0].address); - - expect(hasRole).to.be.true; - }); - - it("should have revoker role", async () => { - const hasRole = await DocumentStoreInstance.hasRole(revokerRole, Accounts[0].address); - - expect(hasRole).to.be.true; - }); - }); - }); - }); - - describe("version", () => { - it("should have a version field value that should be bumped on new versions of the contract", async () => { - const versionFromSolidity = await DocumentStoreInstance.version(); - expect(versionFromSolidity).to.be.equal("2.3.0"); - }); - }); - - describe("issue", () => { - const documentMerkleRoot = "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"; - - it("should be able to issue a document", async () => { - const tx = await DocumentStoreInstance.issue(documentMerkleRoot); - const receipt = await tx.wait(); - - // FIXME: Use a utility helper to watch for event - expect(receipt.events[0].event).to.be.equal("DocumentIssued", "Document issued event not emitted."); - expect(receipt.events[0].args.document).to.be.equal(documentMerkleRoot, "Incorrect event arguments emitted"); - - const issued = await DocumentStoreInstance.isIssued(documentMerkleRoot); - expect(issued, "Document is not issued").to.be.true; - }); - - it("should not allow duplicate issues", async () => { - await DocumentStoreInstance.issue(documentMerkleRoot); - - // Check that reissue is rejected - await expect(DocumentStoreInstance.issue(documentMerkleRoot)).to.be.rejectedWith( - /revert/, - "Duplicate issue was not rejected" - ); - }); - - it("should revert when caller has no issuer role", async () => { - const account = Accounts[1]; - const hasNoIssuerRole = await DocumentStoreInstance.hasRole(issuerRole, account.address); - assert.isFalse(hasNoIssuerRole, "Non-Issuer Account has issuer role"); - - await expect(DocumentStoreInstance.connect(account).issue(documentMerkleRoot)).to.be.rejectedWith( - /AccessControl/ - ); - }); - - it("should issue successfully when caller has issuer role", async () => { - const account = Accounts[0]; - const hasIssuerRole = await DocumentStoreInstance.hasRole(issuerRole, account.address); - assert.isTrue(hasIssuerRole, "Issuer Account has issuer role"); - - await DocumentStoreInstance.connect(account).issue(documentMerkleRoot); - const issued = await DocumentStoreInstance.isIssued(documentMerkleRoot); - - expect(issued).to.be.true; - }); - }); - - describe("bulkIssue", () => { - it("should be able to issue one document", async () => { - const documentMerkleRoots = ["0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"]; - const tx = await DocumentStoreInstance.bulkIssue(documentMerkleRoots); - const receipt = await tx.wait(); - // FIXME: - expect(receipt.events[0].event).to.be.equal("DocumentIssued", "Document issued event not emitted."); - - const document1Issued = await DocumentStoreInstance.isIssued(documentMerkleRoots[0]); - expect(document1Issued, "Document 1 is not issued").to.be.true; - }); - - it("should be able to issue multiple documents", async () => { - const documentMerkleRoots = [ - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330", - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6331", - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6332", - ]; - const tx = await DocumentStoreInstance.bulkIssue(documentMerkleRoots); - const receipt = await tx.wait(); - // FIXME: - expect(receipt.events[0].event).to.be.equal("DocumentIssued", "Document issued event not emitted."); - expect(receipt.events[0].args.document).to.be.equal(documentMerkleRoots[0], "Event not emitted for document 1"); - expect(receipt.events[1].args.document).to.be.equal(documentMerkleRoots[1], "Event not emitted for document 2"); - expect(receipt.events[2].args.document).to.be.equal(documentMerkleRoots[2], "Event not emitted for document 3"); - - const document1Issued = await DocumentStoreInstance.isIssued(documentMerkleRoots[0]); - expect(document1Issued, "Document 1 is not issued").to.be.true; - const document2Issued = await DocumentStoreInstance.isIssued(documentMerkleRoots[1]); - expect(document2Issued, "Document 2 is not issued").to.be.true; - const document3Issued = await DocumentStoreInstance.isIssued(documentMerkleRoots[2]); - expect(document3Issued, "Document 3 is not issued").to.be.true; - }); - - it("should not allow duplicate issues", async () => { - const documentMerkleRoots = [ - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330", - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330", - ]; - - // Check that reissue is rejected - await expect(DocumentStoreInstance.bulkIssue(documentMerkleRoots)).to.be.rejectedWith( - /revert/, - "Duplicate issue was not rejected" - ); - }); - - it("should revert when caller has no issuer role", async () => { - const nonIssuerAccount = Accounts[1]; - const hasNoIssuerRole = await DocumentStoreInstance.hasRole(issuerRole, nonIssuerAccount.address); - assert.isFalse(hasNoIssuerRole, "Non-Issuer Account has issuer role"); - - const documentMerkleRoots = ["0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"]; - - // FIXME: - await expect(DocumentStoreInstance.connect(nonIssuerAccount).bulkIssue(documentMerkleRoots)).to.be.rejectedWith( - /AccessControl/ - ); - }); - - it("should bulk issue successfully when caller has issuer role", async () => { - const documentMerkleRoots = ["0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"]; - - const account = Accounts[0]; - const hasIssuerRole = await DocumentStoreInstance.hasRole(issuerRole, account.address); - assert.isTrue(hasIssuerRole, "Issuer Account has no issuer role"); - - await DocumentStoreInstance.connect(account).bulkIssue(documentMerkleRoots); - const issued = await DocumentStoreInstance.isIssued(documentMerkleRoots[0]); - - expect(issued).to.be.true; - }); - }); - - describe("getIssuedBlock", () => { - it("returns the block number of issued batches", async () => { - const documentMerkleRoot = "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"; - await DocumentStoreInstance.issue(documentMerkleRoot); - - const blockNumber = await DocumentStoreInstance.getIssuedBlock(documentMerkleRoot); - - // chai can't handle BigInts - // eslint-disable-next-line chai-expect/no-inner-compare - expect(BigInt(blockNumber) > 1).to.be.true; - }); - - it("errors on unissued batch", async () => { - const documentMerkleRoot = "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"; - - // This test may fail on ganache-ui (works for ganache-cli) - await expect(DocumentStoreInstance.getIssuedBlock(documentMerkleRoot)).to.be.rejectedWith(/revert/); - }); - }); - - describe("isIssued", () => { - it("should return true for issued batch", async () => { - const documentMerkleRoot = "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"; - await DocumentStoreInstance.issue(documentMerkleRoot); - - const issued = await DocumentStoreInstance.isIssued(documentMerkleRoot); - expect(issued, "Document batch is not issued").to.be.true; - }); - - it("should return false for document batch not issued", async () => { - const documentMerkleRoot = "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"; - - const issued = await DocumentStoreInstance.isIssued(documentMerkleRoot); - expect(issued, "Document batch is issued in error").to.be.false; - }); - }); - - describe("revoke", () => { - const documentMerkleRoot = "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"; - - it("should allow the revocation of a valid and issued document", async () => { - const documentHash = "0x10327d7f904ee3ee0e69d592937be37a33692a78550bd100d635cdea2344e6c7"; - - await DocumentStoreInstance.issue(documentMerkleRoot); - - const tx = await DocumentStoreInstance.revoke(documentHash); - const receipt = await tx.wait(); - - // FIXME: Use a utility helper to watch for event - expect(receipt.events[0].event).to.be.equal("DocumentRevoked"); - expect(receipt.events[0].args.document).to.be.equal(documentHash); - }); - - it("should allow the revocation of an issued root", async () => { - const documentHash = documentMerkleRoot; - - await DocumentStoreInstance.issue(documentMerkleRoot); - - const tx = await DocumentStoreInstance.revoke(documentHash); - const receipt = await tx.wait(); - - // FIXME: Use a utility helper to watch for event - expect(receipt.events[0].event).to.be.equal("DocumentRevoked"); - expect(receipt.events[0].args.document).to.be.equal(documentHash); - }); - - it("should not allow repeated revocation of a valid and issued document", async () => { - const documentHash = "0x10327d7f904ee3ee0e69d592937be37a33692a78550bd100d635cdea2344e6c7"; - - await DocumentStoreInstance.issue(documentMerkleRoot); - - await DocumentStoreInstance.revoke(documentHash); - - await expect(DocumentStoreInstance.revoke(documentHash)).to.be.rejectedWith(/revert/); - }); - - it("should allow revocation of an unissued document", async () => { - const documentHash = "0x10327d7f904ee3ee0e69d592937be37a33692a78550bd100d635cdea2344e6c7"; - - const tx = await DocumentStoreInstance.revoke(documentHash); - const receipt = await tx.wait(); - - // FIXME: Use a utility helper to watch for event - expect(receipt.events[0].event).to.be.equal("DocumentRevoked"); - expect(receipt.events[0].args.document).to.be.equal(documentHash); - }); - - it("should revert when caller has no revoker role", async () => { - const nonRevokerAccount = Accounts[1]; - const hasNoRevokerRole = await DocumentStoreInstance.hasRole(revokerRole, nonRevokerAccount.address); - assert.isFalse(hasNoRevokerRole, "Non-Revoker Account has revoker role"); - - await expect(DocumentStoreInstance.connect(nonRevokerAccount).revoke(documentMerkleRoot)).to.be.rejectedWith( - /AccessControl/ - ); - }); - - it("should revoke successfully when caller has issuer role", async () => { - const account = Accounts[0]; - const hasIssuerRole = await DocumentStoreInstance.hasRole(issuerRole, account.address); - assert.isTrue(hasIssuerRole, "Revoker Account has no revoker role"); - - await DocumentStoreInstance.connect(account).revoke(documentMerkleRoot); - const issued = await DocumentStoreInstance.isRevoked(documentMerkleRoot); - - expect(issued).to.be.true; - }); - }); - - describe("bulkRevoke", () => { - it("should be able to revoke one document", async () => { - const documentMerkleRoots = ["0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"]; - const tx = await DocumentStoreInstance.bulkRevoke(documentMerkleRoots); - const receipt = await tx.wait(); - - // FIXME: - expect(receipt.events[0].event).to.be.equal("DocumentRevoked", "Document revoked event not emitted."); - - const document1Revoked = await DocumentStoreInstance.isRevoked(documentMerkleRoots[0]); - expect(document1Revoked, "Document 1 is not revoked").to.be.true; - }); - - it("should be able to revoke multiple documents", async () => { - const documentMerkleRoots = [ - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330", - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6331", - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6332", - ]; - const tx = await DocumentStoreInstance.bulkRevoke(documentMerkleRoots); - const receipt = await tx.wait(); - - // FIXME: - expect(receipt.events[0].event).to.be.equal("DocumentRevoked", "Document revoked event not emitted."); - expect(receipt.events[0].args.document).to.be.equal(documentMerkleRoots[0], "Event not emitted for document 1"); - expect(receipt.events[1].args.document).to.be.equal(documentMerkleRoots[1], "Event not emitted for document 2"); - expect(receipt.events[2].args.document).to.be.equal(documentMerkleRoots[2], "Event not emitted for document 3"); - - const document1Revoked = await DocumentStoreInstance.isRevoked(documentMerkleRoots[0]); - expect(document1Revoked, "Document 1 is not revoked").to.be.true; - const document2Revoked = await DocumentStoreInstance.isRevoked(documentMerkleRoots[1]); - expect(document2Revoked, "Document 2 is not revoked").to.be.true; - const document3Revoked = await DocumentStoreInstance.isRevoked(documentMerkleRoots[2]); - expect(document3Revoked, "Document 3 is not revoked").to.be.true; - }); - - it("should not allow duplicate revokes", async () => { - const documentMerkleRoots = [ - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330", - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330", - ]; - - // Check that revoke is rejected - await expect(DocumentStoreInstance.bulkRevoke(documentMerkleRoots)).to.be.rejectedWith( - /revert/, - "Duplicate revoke was not rejected" - ); - }); - - it("should revert when caller has no revoker role", async () => { - const nonRevokerAccount = Accounts[1]; - const hasNoRevokerRole = await DocumentStoreInstance.hasRole(revokerRole, nonRevokerAccount.address); - assert.isFalse(hasNoRevokerRole, "Non-Revoker Account has revoker role"); - - const documentMerkleRoots = ["0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"]; - await expect(DocumentStoreInstance.connect(nonRevokerAccount).bulkRevoke(documentMerkleRoots)).to.be.rejectedWith( - /AccessControl/ - ); - }); - - it("should bulk revoke successfully when caller has issuer role", async () => { - const documentMerkleRoots = ["0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"]; - - const account = Accounts[0]; - const hasIssuerRole = await DocumentStoreInstance.hasRole(issuerRole, account.address); - assert.isTrue(hasIssuerRole, "Revoker Account has no revoker role"); - - await DocumentStoreInstance.connect(account).bulkRevoke(documentMerkleRoots); - const issued = await DocumentStoreInstance.isRevoked(documentMerkleRoots[0]); - - expect(issued).to.be.true; - }); - }); - - describe("isRevoked", () => { - it("returns true for revoked documents", async () => { - const documentMerkleRoot = "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"; - const documentHash = "0x10327d7f904ee3ee0e69d592937be37a33692a78550bd100d635cdea2344e6c7"; - - await DocumentStoreInstance.issue(documentMerkleRoot); - await DocumentStoreInstance.revoke(documentHash); - - const revoked = await DocumentStoreInstance.isRevoked(documentHash); - expect(revoked).to.be.true; - }); - - it("returns true for non-revoked documents", async () => { - "0x3a267813bea8120f55a7b9ca814c34dd89f237502544d7c75dfd709a659f6330"; - - const documentHash = "0x10327d7f904ee3ee0e69d592937be37a33692a78550bd100d635cdea2344e6c7"; - - const revoked = await DocumentStoreInstance.isRevoked(documentHash); - expect(revoked).to.be.false; - }); - }); - - describe("isRevokedBefore", () => { - const documentHash = "0x10327d7f904ee3ee0e69d592937be37a33692a78550bd100d635cdea2344e6c7"; - - it("returns false for document revoked after the block number", async () => { - const tx = await DocumentStoreInstance.revoke(documentHash); - const receipt = await tx.wait(); - const revokedBlock = get(receipt, "blockNumber"); - const revoked = await DocumentStoreInstance.isRevokedBefore(documentHash, revokedBlock - 1); - expect(revoked).to.be.false; - }); - - it("returns true for document revoked at the block number", async () => { - const tx = await DocumentStoreInstance.revoke(documentHash); - const receipt = await tx.wait(); - const revokedBlock = get(receipt, "blockNumber"); - const revoked = await DocumentStoreInstance.isRevokedBefore(documentHash, revokedBlock); - expect(revoked).to.be.true; - }); - - it("returns true for document revoked before the block number", async () => { - const tx = await DocumentStoreInstance.revoke(documentHash); - const receipt = await tx.wait(); - const revokedBlock = get(receipt, "blockNumber"); - const revoked = await DocumentStoreInstance.isRevokedBefore(documentHash, revokedBlock + 1); - expect(revoked).to.be.true; - }); - - it("returns false for document not revoked, for arbitary block number", async () => { - const revoked = await DocumentStoreInstance.isRevokedBefore(documentHash, 1000); - expect(revoked).to.be.false; - }); - - it("returns false for document not revoked, for block 0", async () => { - const revoked = await DocumentStoreInstance.isRevokedBefore(documentHash, 0); - expect(revoked).to.be.false; - }); - }); - - describe("isIssuedBefore", () => { - const documentHash = "0x10327d7f904ee3ee0e69d592937be37a33692a78550bd100d635cdea2344e6c7"; - - it("returns false for document issued after the block number", async () => { - const tx = await DocumentStoreInstance.issue(documentHash); - const receipt = await tx.wait(); - const issuedBlock = get(receipt, "blockNumber"); - const issued = await DocumentStoreInstance.isIssuedBefore(documentHash, issuedBlock - 1); - expect(issued).to.be.false; - }); - - it("returns true for document issued at the block number", async () => { - const tx = await DocumentStoreInstance.issue(documentHash); - const receipt = await tx.wait(); - const issuedBlock = get(receipt, "blockNumber"); - const issued = await DocumentStoreInstance.isIssuedBefore(documentHash, issuedBlock); - expect(issued).to.be.true; - }); - - it("returns true for document issued before the block number", async () => { - const tx = await DocumentStoreInstance.issue(documentHash); - const receipt = await tx.wait(); - const issuedBlock = get(receipt, "blockNumber"); - const issued = await DocumentStoreInstance.isIssuedBefore(documentHash, issuedBlock + 1); - expect(issued).to.be.true; - }); - - it("returns false for document not issued, for arbitary block number", async () => { - const issued = await DocumentStoreInstance.isIssuedBefore(documentHash, 1000); - expect(issued).to.be.false; - }); - - it("returns false for document not issued, for block 0", async () => { - const issued = await DocumentStoreInstance.isIssuedBefore(documentHash, 0); - expect(issued).to.be.false; - }); - }); -}); diff --git a/test/DocumentStoreCreator.js b/test/DocumentStoreCreator.js deleted file mode 100644 index f38c061..0000000 --- a/test/DocumentStoreCreator.js +++ /dev/null @@ -1,37 +0,0 @@ -const { expect } = require("chai").use(require("chai-as-promised")); -const { ethers } = require("hardhat"); -const config = require("../config.js"); - -describe("DocumentStoreCreator", async () => { - let Accounts; - let DocumentStore; - let DocumentStoreCreator; - - before("", async () => { - Accounts = await ethers.getSigners(); - DocumentStore = await ethers.getContractFactory("DocumentStore"); - DocumentStoreCreator = await ethers.getContractFactory("DocumentStoreCreator"); - }); - - let DocumentStoreCreatorInstance; - - beforeEach("", async () => { - DocumentStoreCreatorInstance = await DocumentStoreCreator.connect(Accounts[0]).deploy(); - }); - - describe("deploy", () => { - it("should deploy new instance of DocumentStore correctly", async () => { - // Test for events emitted by factory - const tx = await DocumentStoreCreatorInstance.deploy(config.INSTITUTE_NAME); - const receipt = await tx.wait(); - expect(receipt.logs[4].args[1]).to.be.equal(Accounts[0].address, "Emitted contract creator does not match"); - // Test correctness of deployed DocumentStore - const deployedDocumentStore = await DocumentStore.attach(receipt.logs[4].args[0]); - const name = await deployedDocumentStore.name(); - expect(name).to.be.equal(config.INSTITUTE_NAME, "Name of institute does not match"); - - const hasAdminRole = await deployedDocumentStore.hasRole(ethers.ZeroHash, Accounts[0].address); - expect(hasAdminRole).to.be.true; - }); - }); -});