Skip to content

Commit

Permalink
fix: bring back original document store
Browse files Browse the repository at this point in the history
  • Loading branch information
Nebulis authored Apr 13, 2020
1 parent fd06d43 commit a31f3b8
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 42 deletions.
22 changes: 11 additions & 11 deletions benchmark/GasCostBenchmark.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const {groupBy, mapValues} = require("lodash");

const DocumentStore = artifacts.require("./DocumentStore.sol");
const UpgradableDocumentStore = artifacts.require("./UpgradableDocumentStore.sol");
const DocumentStoreWithRevokeReasons = artifacts.require("./DocumentStoreWithRevokeReasons.sol");
const ProxyFactory = artifacts.require("./ProxyFactory.sol");
const DocumentStoreCreator = artifacts.require("./DocumentStoreCreator.sol");
const BaseAdminUpgradeabilityProxy = artifacts.require("./BaseAdminUpgradeabilityProxy.sol");

DocumentStore.numberFormat = "String";
UpgradableDocumentStore.numberFormat = "String";
const {generateHashes} = require("../scripts/generateHashes");

const initializeAbi = {
Expand Down Expand Up @@ -124,24 +124,24 @@ describe("Gas Cost Benchmarks", () => {
console.table(records);
});

let staticDocumentStoreInstance;
let staticUpgradableDocumentStoreInstance;
let staticProxyFactoryInstance;
let staticDocumentStoreWithRevokeReasons;

before(async () => {
staticDocumentStoreInstance = await DocumentStore.new();
staticUpgradableDocumentStoreInstance = await UpgradableDocumentStore.new();
staticProxyFactoryInstance = await ProxyFactory.new();
staticDocumentStoreWithRevokeReasons = await DocumentStoreWithRevokeReasons.new();
});

contract(
"DocumentStore",
"UpgradableDocumentStore",
accounts => {
const contractName = "DocumentStore";
const contractName = "UpgradableDocumentStore";

it("runs benchmark", async () => {
// Deploy & initialize document store contract
const documentStoreInstance = await DocumentStore.new();
const documentStoreInstance = await UpgradableDocumentStore.new();
const deploymentReceipt = await web3.eth.getTransactionReceipt(documentStoreInstance.transactionHash);
const initializeReceipt = await documentStoreInstance.initialize(STORE_NAME, accounts[0]);
recordGasCost(
Expand Down Expand Up @@ -185,13 +185,13 @@ describe("Gas Cost Benchmarks", () => {
// Deploy & initialize document store contract in one transaction
const encodedInitializeCall = web3.eth.abi.encodeFunctionCall(initializeAbi, [STORE_NAME, accounts[0]]);
const deployTx = await staticProxyFactoryInstance.deployMinimal(
staticDocumentStoreInstance.address,
staticUpgradableDocumentStoreInstance.address,
encodedInitializeCall
);
const minimalProxyAddress = deployTx.logs[0].args.proxy;
recordGasCost(contractName, "deployment", deployTx.receipt.cumulativeGasUsed);

const proxiedDocumentStoreInstance = await DocumentStore.at(minimalProxyAddress);
const proxiedDocumentStoreInstance = await UpgradableDocumentStore.at(minimalProxyAddress);

await benchmarkTransfer(contractName, proxiedDocumentStoreInstance, accounts);
await benchmarkIssue(contractName, proxiedDocumentStoreInstance);
Expand All @@ -214,14 +214,14 @@ describe("Gas Cost Benchmarks", () => {
const salt = 1337;
const deployTx = await staticProxyFactoryInstance.deploy(
salt,
staticDocumentStoreInstance.address,
staticUpgradableDocumentStoreInstance.address,
accounts[1], // Must use separate account for proxy admin
encodedInitializeCall
);
recordGasCost(contractName, "deployment", deployTx.receipt.cumulativeGasUsed);

const adminUpgradableProxyAddress = deployTx.logs[0].args.proxy;
const proxiedDocumentStoreInstance = await DocumentStore.at(adminUpgradableProxyAddress);
const proxiedDocumentStoreInstance = await UpgradableDocumentStore.at(adminUpgradableProxyAddress);

await benchmarkTransfer(contractName, proxiedDocumentStoreInstance, accounts);
await benchmarkIssue(contractName, proxiedDocumentStoreInstance);
Expand Down
13 changes: 5 additions & 8 deletions contracts/DocumentStore.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
pragma solidity 0.5.12;
pragma solidity ^0.5.12;

import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
import "./Ownable.sol";

contract DocumentStore is Initializable, Ownable {
contract DocumentStore is Ownable {
string public name;
string public version;
string public version = "2.3.0";

/// A mapping of the document hash to the block number that was issued
mapping(bytes32 => uint256) public documentIssued;
Expand All @@ -15,9 +14,7 @@ contract DocumentStore is Initializable, Ownable {
event DocumentIssued(bytes32 indexed document);
event DocumentRevoked(bytes32 indexed document);

function initialize(string memory _name, address owner) public initializer {
super.initialize(owner);
version = "3.0.0";
constructor(string memory _name) public {
name = _name;
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/DocumentStoreCreator.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity 0.5.12;

import "./DocumentStore.sol";
import "./UpgradableDocumentStore.sol";

// 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
Expand All @@ -10,7 +10,7 @@ contract DocumentStoreCreator {

function deploy(string memory name) public returns (address) {
// solhint-disable-next-line mark-callable-contracts
DocumentStore instance = new DocumentStore();
UpgradableDocumentStore instance = new UpgradableDocumentStore();
instance.initialize(name, msg.sender);
emit DocumentStoreDeployed(address(instance), msg.sender);
return address(instance);
Expand Down
4 changes: 2 additions & 2 deletions contracts/DocumentStoreWithRevokeReasons.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
pragma solidity 0.5.12;

import "./DocumentStore.sol";
import "./UpgradableDocumentStore.sol";

contract DocumentStoreWithRevokeReasons is DocumentStore {
contract DocumentStoreWithRevokeReasons is UpgradableDocumentStore {
/// A mapping of the document hash to the block number that was issued
mapping(bytes32 => uint256) public revokeReason;

Expand Down
75 changes: 75 additions & 0 deletions contracts/Ownable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
pragma solidity ^0.5.12;

/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}

/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}

/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}

/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}

/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}

/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}

/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
80 changes: 80 additions & 0 deletions contracts/UpgradableDocumentStore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
pragma solidity 0.5.12;

import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";

contract UpgradableDocumentStore is Initializable, Ownable {
string public name;
string public version;

/// A mapping of the document hash to the block number that was issued
mapping(bytes32 => uint256) public documentIssued;
/// A mapping of the hash of the claim being revoked to the revocation block number
mapping(bytes32 => uint256) public documentRevoked;

event DocumentIssued(bytes32 indexed document);
event DocumentRevoked(bytes32 indexed document);

function initialize(string memory _name, address owner) public initializer {
super.initialize(owner);
version = "3.0.0";
name = _name;
}

function issue(bytes32 document) public onlyOwner onlyNotIssued(document) {
documentIssued[document] = block.number;
emit DocumentIssued(document);
}

function bulkIssue(bytes32[] memory documents) public {
for (uint256 i = 0; i < documents.length; i++) {
issue(documents[i]);
}
}

function getIssuedBlock(bytes32 document) public view onlyIssued(document) returns (uint256) {
return documentIssued[document];
}

function isIssued(bytes32 document) public view returns (bool) {
return (documentIssued[document] != 0);
}

function isIssuedBefore(bytes32 document, uint256 blockNumber) public view returns (bool) {
return documentIssued[document] != 0 && documentIssued[document] <= blockNumber;
}

function revoke(bytes32 document) public onlyOwner onlyNotRevoked(document) returns (bool) {
documentRevoked[document] = block.number;
emit DocumentRevoked(document);
}

function bulkRevoke(bytes32[] memory documents) public {
for (uint256 i = 0; i < documents.length; i++) {
revoke(documents[i]);
}
}

function isRevoked(bytes32 document) public view returns (bool) {
return documentRevoked[document] != 0;
}

function isRevokedBefore(bytes32 document, uint256 blockNumber) public view returns (bool) {
return documentRevoked[document] <= blockNumber && documentRevoked[document] != 0;
}

modifier onlyIssued(bytes32 document) {
require(isIssued(document), "Error: Only issued document hashes can be revoked");
_;
}

modifier onlyNotIssued(bytes32 document) {
require(!isIssued(document), "Error: Only hashes that have not been issued can be issued");
_;
}

modifier onlyNotRevoked(bytes32 claim) {
require(!isRevoked(claim), "Error: Hash has been revoked previously");
_;
}
}
4 changes: 2 additions & 2 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ beforeAll(async () => {
});

describe("deploy", () => {
it("deploys a new DocumentStore contract without waiting for confirmation", async () => {
it("deploys a new UpgradableDocumentStore contract without waiting for confirmation", async () => {
const receipt = await deploy("My Store", signer, {documentStoreCreatorAddressOverride});
expect(receipt.from).toBe(account);
});
});

describe("deployAndWait", () => {
it("deploys a new DocumentStore contract", async () => {
it("deploys a new UpgradableDocumentStore contract", async () => {
const instance = await deployAndWait("My Store", signer, {documentStoreCreatorAddressOverride});
const owner = await instance.owner();
expect(owner).toBe(account);
Expand Down
7 changes: 4 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Signer, providers, ContractTransaction} from "ethers";
import {DocumentStoreFactory} from "./contracts/DocumentStoreFactory";
import {UpgradableDocumentStoreFactory} from "./contracts/UpgradableDocumentStoreFactory";
import {DocumentStoreCreatorFactory} from "./contracts/DocumentStoreCreatorFactory";
import {getDocumentStoreCreatorAddress} from "./config";

Expand All @@ -20,12 +20,13 @@ export const deploy = async (name: string, signer: Signer, options?: DeployOptio
export const deployAndWait = async (name: string, signer: Signer, options?: DeployOptions) => {
const receipt = await (await deploy(name, signer, options)).wait();
if (!receipt.logs || !receipt.logs[0].address) throw new Error("Fail to detect deployed contract address");
return DocumentStoreFactory.connect(receipt.logs![0].address, signer);
return UpgradableDocumentStoreFactory.connect(receipt.logs![0].address, signer);
};

export const connect = async (address: string, signerOrProvider: Signer | providers.Provider) => {
return DocumentStoreFactory.connect(address, signerOrProvider);
return UpgradableDocumentStoreFactory.connect(address, signerOrProvider);
};

export {UpgradableDocumentStoreFactory} from "./contracts/UpgradableDocumentStoreFactory";
export {DocumentStoreFactory} from "./contracts/DocumentStoreFactory";
export {DocumentStoreCreatorFactory} from "./contracts/DocumentStoreCreatorFactory";
8 changes: 4 additions & 4 deletions test/DocumentStore.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
const DocumentStore = artifacts.require("./DocumentStore.sol");
DocumentStore.numberFormat = "String";
const UpgradableDocumentStore = artifacts.require("./UpgradableDocumentStore.sol");
UpgradableDocumentStore.numberFormat = "String";
const {get} = require("lodash");

const {expect} = require("chai").use(require("chai-as-promised"));
const config = require("../config.js");

contract("DocumentStore", accounts => {
contract("UpgradableDocumentStore", accounts => {
let instance = null;

// Related: https://github.com/trufflesuite/truffle-core/pull/98#issuecomment-360619561
beforeEach(async () => {
instance = await DocumentStore.new();
instance = await UpgradableDocumentStore.new();
await instance.initialize(config.INSTITUTE_NAME, accounts[0]);
});

Expand Down
4 changes: 2 additions & 2 deletions test/DocumentStoreCreator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const DocumentStore = artifacts.require("./DocumentStore.sol");
const UpgradableDocumentStore = artifacts.require("./UpgradableDocumentStore.sol");
const DocumentStoreCreator = artifacts.require("./DocumentStoreCreator.sol");

const {expect} = require("chai").use(require("chai-as-promised"));
Expand All @@ -14,7 +14,7 @@ contract("DocumentStoreCreator", accounts => {
expect(deployReceipt.logs[0].args.creator).to.be.equal(accounts[1], "Emitted contract creator does not match");

// Test correctness of deployed DocumentStore
const deployedDocumentStore = await DocumentStore.at(deployReceipt.logs[0].args.instance);
const deployedDocumentStore = await UpgradableDocumentStore.at(deployReceipt.logs[0].args.instance);
const name = await deployedDocumentStore.name();
expect(name).to.be.equal(config.INSTITUTE_NAME, "Name of institute does not match");
const owner = await deployedDocumentStore.owner();
Expand Down
Loading

0 comments on commit a31f3b8

Please sign in to comment.