Skip to content

Commit

Permalink
feat: implement verifier manager v2
Browse files Browse the repository at this point in the history
  • Loading branch information
Nebulis committed Dec 12, 2019
1 parent acbd650 commit 661d150
Show file tree
Hide file tree
Showing 21 changed files with 208 additions and 361 deletions.
5 changes: 3 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"import/no-unresolved": "off",
"import/prefer-default-export": "off",
"import/extensions": "off",
"@typescript-eslint/explicit-function-return-type":"off",
"@typescript-eslint/no-explicit-any":"off"
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"no-unused-expressions": "off"
}
}
5 changes: 1 addition & 4 deletions src/common/smartContract/contractInstance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ it("creates a ethers.Contract instance with the right provider", () => {
});

// @ts-ignore
expect(ethers.providers.InfuraProvider.mock.calls[0]).toEqual([
"NETWORK",
"INFURA_API_KEY"
]);
expect(ethers.providers.InfuraProvider.mock.calls[0]).toEqual(["NETWORK", "INFURA_API_KEY"]);

// @ts-ignore
expect(ethers.Contract.mock.calls[0][0]).toEqual("0x0A");
Expand Down
5 changes: 3 additions & 2 deletions src/common/smartContract/contractInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ interface ContractInstance {
abi: any; // type is any of json file in abi folder
network: string;
contractAddress: Hash;
infuraApiKey?: string;
}
export const contractInstance = ({ abi, network, contractAddress }: ContractInstance) => {
const provider = new ethers.providers.InfuraProvider(network, INFURA_API_KEY);
export const contractInstance = ({ abi, network, contractAddress, infuraApiKey }: ContractInstance) => {
const provider = new ethers.providers.InfuraProvider(network, infuraApiKey || INFURA_API_KEY);
return new ethers.Contract(contractAddress, abi, provider);
};
4 changes: 2 additions & 2 deletions src/common/smartContract/documentToSmartContracts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ it("returns an array of smart contract resolved from the issuers of the document
];
// @ts-ignore
issuerToSmartContract.mockReturnValue(expectedValue[0]);
const results = documentToSmartContracts(document, network);
const results = documentToSmartContracts(document, { network });
// @ts-ignore
expect(issuerToSmartContract.mock.calls[0]).toEqual([
{
name: "Singapore Examination and Assessment Board",
url: "https://www.seab.gov.sg/",
certificateStore: "0x20bc9C354A18C8178A713B9BcCFFaC2152b53990"
},
network
{ network }
]);
expect(results).toEqual(expectedValue);
});
10 changes: 3 additions & 7 deletions src/common/smartContract/documentToSmartContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ import { Document, getData } from "@govtechsg/open-attestation";
import { issuerToSmartContract } from "./issuerToSmartContract";
import { Issuer } from "../../types/core";

// Given a list of issuers, convert to smart contract
const mapIssuersToSmartContracts = (issuers: Issuer[], network: string) =>
issuers.map(issuer => issuerToSmartContract(issuer, network));

// Given a raw document, return list of all smart contracts
export const documentToSmartContracts = (document: Document, network: string) => {
export const documentToSmartContracts = (document: Document, options: { network: string; infuraApiKey?: string }) => {
const data = getData(document);
const issuers = data.issuers || [];
const issuers: Issuer[] = data.issuers || [];

return mapIssuersToSmartContracts(issuers, network);
return issuers.map(issuer => issuerToSmartContract(issuer, options));
};
8 changes: 4 additions & 4 deletions src/common/smartContract/issuerToSmartContract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ it("maps issuer with certificateStore to documentStore contract", () => {
certificateStore: "0xc36484efa1544c32ffed2e80a1ea9f0dfc517495"
};

const result = issuerToSmartContract(issuer, "homestead");
const result = issuerToSmartContract(issuer, { network: "homestead" });
expect(result).toEqual(
expect.objectContaining({
type: "DOCUMENT_STORE",
Expand All @@ -20,7 +20,7 @@ it("maps issuer with documentStore to documentStore contract", () => {
name: "Org A",
documentStore: "0xc36484efa1544c32ffed2e80a1ea9f0dfc517495"
};
const result = issuerToSmartContract(issuer, "homestead");
const result = issuerToSmartContract(issuer, { network: "homestead" });
expect(result).toEqual(
expect.objectContaining({
type: "DOCUMENT_STORE",
Expand All @@ -34,7 +34,7 @@ it("maps issuer with tokenRegistry to tokenRegistry contract", () => {
name: "Org A",
tokenRegistry: "0xc36484efa1544c32ffed2e80a1ea9f0dfc517495"
};
const result = issuerToSmartContract(issuer, "homestead");
const result = issuerToSmartContract(issuer, { network: "homestead" });
expect(result).toEqual(
expect.objectContaining({
type: "TOKEN_REGISTRY",
Expand All @@ -49,7 +49,7 @@ it("throws if the issuer does not have a valid smart contract address defined",
foo: "bar"
};
// @ts-ignore
expect(() => issuerToSmartContract(issuer, "homestead")).toThrow(
expect(() => issuerToSmartContract(issuer, { network: "homestead" })).toThrow(
"Issuer does not have a smart contract"
);
});
14 changes: 10 additions & 4 deletions src/common/smartContract/issuerToSmartContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import tokenRegistryAbi from "./abi/tokenRegistry.json";
import documentStoreAbi from "./abi/documentStore.json";
import { Issuer, OpenAttestationContract } from "../../types/core";

export const issuerToSmartContract = (issuer: Issuer, network: string): OpenAttestationContract => {
export const issuerToSmartContract = (
issuer: Issuer,
options: { network: string; infuraApiKey?: string }
): OpenAttestationContract => {
switch (true) {
case "tokenRegistry" in issuer:
return {
Expand All @@ -14,7 +17,8 @@ export const issuerToSmartContract = (issuer: Issuer, network: string): OpenAtte
instance: contractInstance({
contractAddress: issuer.tokenRegistry!,
abi: tokenRegistryAbi,
network
network: options.network,
infuraApiKey: options.infuraApiKey
})
};
case "certificateStore" in issuer:
Expand All @@ -24,7 +28,8 @@ export const issuerToSmartContract = (issuer: Issuer, network: string): OpenAtte
instance: contractInstance({
contractAddress: issuer.certificateStore!,
abi: documentStoreAbi,
network
network: options.network,
infuraApiKey: options.infuraApiKey
})
};
case "documentStore" in issuer:
Expand All @@ -34,7 +39,8 @@ export const issuerToSmartContract = (issuer: Issuer, network: string): OpenAtte
instance: contractInstance({
contractAddress: issuer.documentStore!,
abi: documentStoreAbi,
network
network: options.network,
infuraApiKey: options.infuraApiKey
})
};
default:
Expand Down
5 changes: 1 addition & 4 deletions src/hash/hash.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
verifySignature,
SchematisedDocument
} from "@govtechsg/open-attestation";
import { verifySignature, SchematisedDocument } from "@govtechsg/open-attestation";

export const verifyHash = async (document: SchematisedDocument) => ({
checksumMatch: await verifySignature(document)
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const defaultVerifiers: Verifier[] = [
openAttestationEthereumDocumentStoreRevoked,
openAttestationDnsTxtIdentity
];

const defaultVerificationManager = verificationManager(defaultVerifiers);

export {
Expand Down
41 changes: 13 additions & 28 deletions src/issued/contractInterface.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { constants } from "ethers";
import {
isIssued,
isIssuedOnDocumentStore,
isIssuedOnTokenRegistry
} from "./contractInterface";
import { isIssued, isIssuedOnDocumentStore, isIssuedOnTokenRegistry } from "./contractInterface";
import { issuerToSmartContract } from "../common/smartContract/issuerToSmartContract";

describe("isIssuedOnTokenRegistry", () => {
it("returns true if token is created on tokenRegistry", async () => {
const smartContract = issuerToSmartContract(
{ tokenRegistry: "0x48399Fb88bcD031C556F53e93F690EEC07963Af3" },
"ropsten"
{ network: "ropsten" }
);
const issued = await isIssuedOnTokenRegistry(
smartContract,
Expand All @@ -22,19 +18,17 @@ describe("isIssuedOnTokenRegistry", () => {
it("allows error to bubble if token is nonexistent on tokenRegistry", async () => {
const smartContract = issuerToSmartContract(
{ tokenRegistry: "0x48399Fb88bcD031C556F53e93F690EEC07963Af3" },
"ropsten"
{ network: "ropsten" }
);
await expect(
isIssuedOnTokenRegistry(smartContract, constants.HashZero)
).rejects.toThrow();
await expect(isIssuedOnTokenRegistry(smartContract, constants.HashZero)).rejects.toThrow();
});
});

describe("isIssuedOnDocumentStore", () => {
it("returns true if document is issued on documentStore", async () => {
const smartContract = issuerToSmartContract(
{ documentStore: "0x008486e2b14Cb1B5231DbA10B2170271af3196d6" },
"ropsten"
{ network: "ropsten" }
);
const issued = await isIssuedOnDocumentStore(
smartContract,
Expand All @@ -47,41 +41,34 @@ describe("isIssuedOnDocumentStore", () => {
const hash = constants.HashZero;
const smartContract = issuerToSmartContract(
{ documentStore: "0x008486e2b14Cb1B5231DbA10B2170271af3196d6" },
"ropsten"
{ network: "ropsten" }
);
const issued = await isIssuedOnDocumentStore(smartContract, hash);
expect(issued).toBe(false);
});

it("allows error to bubble if documentStore is not deployed", async () => {
const smartContract = issuerToSmartContract(
{ documentStore: constants.AddressZero },
"ropsten"
);
await expect(
isIssuedOnDocumentStore(smartContract, constants.HashZero)
).rejects.toThrow();
const smartContract = issuerToSmartContract({ documentStore: constants.AddressZero }, { network: "ropsten" });
await expect(isIssuedOnDocumentStore(smartContract, constants.HashZero)).rejects.toThrow();
});
});

describe("isIssued", () => {
it("works for tokenRegistry", async () => {
const smartContract = issuerToSmartContract(
{ tokenRegistry: "0x48399Fb88bcD031C556F53e93F690EEC07963Af3" },
"ropsten"
{ network: "ropsten" }
);
const hash =
"0x30cc3db1f2b26e25d63a67b6f232c4cf2acd1402f632847a4857e73516a0762f";
const hash = "0x30cc3db1f2b26e25d63a67b6f232c4cf2acd1402f632847a4857e73516a0762f";
const issued = await isIssued(smartContract, hash);
expect(issued).toBe(true);
});

it("works for documentStore", async () => {
const hash =
"0x85df2b4e905a82cf10c317df8f4b659b5cf38cc12bd5fbaffba5fc901ef0011b";
const hash = "0x85df2b4e905a82cf10c317df8f4b659b5cf38cc12bd5fbaffba5fc901ef0011b";
const smartContract = issuerToSmartContract(
{ documentStore: "0x008486e2b14Cb1B5231DbA10B2170271af3196d6" },
"ropsten"
{ network: "ropsten" }
);
const issued = await isIssued(smartContract, hash);
expect(issued).toBe(true);
Expand All @@ -90,8 +77,6 @@ describe("isIssued", () => {
it("throws for unsupported smart contract types", () => {
const smartContract = { type: "UNSUPPORTED_TYPE" };
// @ts-ignore
expect(() => isIssued(smartContract, constants.HashZero)).toThrow(
"Smart contract type not supported"
);
expect(() => isIssued(smartContract, constants.HashZero)).toThrow("Smart contract type not supported");
});
});
20 changes: 4 additions & 16 deletions src/issued/verify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,8 @@ describe("verifyIssued", () => {
});
// @ts-ignore
expect(isIssued.mock.calls).toEqual([
[
{ address: "0x0A", type: "type", instance: contract, foo: "bar" },
"0xMERKLE_ROOT"
],
[
{ address: "0x0B", type: "type", instance: contract, foo: "bar" },
"0xMERKLE_ROOT"
]
[{ address: "0x0A", type: "type", instance: contract, foo: "bar" }, "0xMERKLE_ROOT"],
[{ address: "0x0B", type: "type", instance: contract, foo: "bar" }, "0xMERKLE_ROOT"]
]);
});

Expand Down Expand Up @@ -143,14 +137,8 @@ describe("verifyIssued", () => {
});
// @ts-ignore
expect(isIssued.mock.calls).toEqual([
[
{ address: "0x0A", type: "type", instance: contract, foo: "bar" },
"0xMERKLE_ROOT"
],
[
{ address: "0x0B", type: "type", instance: contract, foo: "bar" },
"0xMERKLE_ROOT"
]
[{ address: "0x0A", type: "type", instance: contract, foo: "bar" }, "0xMERKLE_ROOT"],
[{ address: "0x0B", type: "type", instance: contract, foo: "bar" }, "0xMERKLE_ROOT"]
]);
});
});
Loading

0 comments on commit 661d150

Please sign in to comment.