From 278723e4e52ef0fa962652005c49fe61adf967e4 Mon Sep 17 00:00:00 2001 From: Nebulis Date: Wed, 3 Jun 2020 17:33:59 +0800 Subject: [PATCH] feat: add revocation store fragment --- .../documentStoreContractInterface.ts | 6 +- src/types/error.ts | 8 ++ .../openAttestationRevocationStore.test.ts | 75 +++++++++++++++++++ .../openAttestationRevocationStore.ts | 34 +++++++++ .../revocationStore/revocationStore.ts | 51 +++++++++++++ 5 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 src/verifiers/revocationStore/openAttestationRevocationStore.test.ts create mode 100644 src/verifiers/revocationStore/openAttestationRevocationStore.ts create mode 100644 src/verifiers/revocationStore/revocationStore.ts diff --git a/src/common/smartContract/documentStoreContractInterface.ts b/src/common/smartContract/documentStoreContractInterface.ts index 6c31361c..d418428d 100644 --- a/src/common/smartContract/documentStoreContractInterface.ts +++ b/src/common/smartContract/documentStoreContractInterface.ts @@ -9,7 +9,11 @@ export const getIssuersDocumentStore = ( ): string[] => { if (utils.isWrappedV2Document(document)) { const data = getData(document); - return data.issuers.map((issuer) => issuer.documentStore || issuer.certificateStore || ""); + return data.issuers.map( + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + (issuer) => issuer.documentStore || issuer.certificateStore || issuer.revocationStore || "" + ); } return [getData(document).proof.value]; }; diff --git a/src/types/error.ts b/src/types/error.ts index afe86e13..605cb263 100644 --- a/src/types/error.ts +++ b/src/types/error.ts @@ -22,6 +22,14 @@ export enum OpenAttestationEthereumDocumentStoreRevokedCode { SKIPPED = 4, CONTRACT_NOT_FOUND = 404, } +export enum OpenAttestationRevocationCode { + UNEXPECTED_ERROR = 0, + DOCUMENT_REVOKED = 1, + CONTRACT_ADDRESS_INVALID = 2, + ETHERS_UNHANDLED_ERROR = 3, + SKIPPED = 4, + CONTRACT_NOT_FOUND = 404, +} export enum OpenAttestationEthereumTokenRegistryMintedCode { UNEXPECTED_ERROR = 0, DOCUMENT_NOT_MINTED = 1, diff --git a/src/verifiers/revocationStore/openAttestationRevocationStore.test.ts b/src/verifiers/revocationStore/openAttestationRevocationStore.test.ts new file mode 100644 index 00000000..284e8bc2 --- /dev/null +++ b/src/verifiers/revocationStore/openAttestationRevocationStore.test.ts @@ -0,0 +1,75 @@ +import { openAttestationRevocationStore } from "./openAttestationRevocationStore"; +import { verificationBuilder } from "../verificationBuilder"; +import { documentRopstenValidWithDocumentStore } from "../../../test/fixtures/v2/documentRopstenValidWithDocumentStore"; +import { documentRopstenRevocationStoreNotRevoked, documentRopstenRevocationStoreRevoked } from "./revocationStore"; + +const verify = verificationBuilder([openAttestationRevocationStore]); +describe("OpenAttestationRevocationStore", () => { + describe("v2", () => { + it("should return a skipped fragment when document store is used", async () => { + const fragment = await verify(documentRopstenValidWithDocumentStore, { network: "ropsten" }); + expect(fragment).toStrictEqual([ + { + name: "OpenAttestationRevocationStore", + type: "DOCUMENT_STATUS", + reason: { + code: 4, + codeString: "SKIPPED", + message: 'Document issuers doesn\'t have "revocationStore"', + }, + status: "SKIPPED", + }, + ]); + }); + it("should return a valid fragment when revocation store is used and the document is not revoked", async () => { + const fragment = await verify(documentRopstenRevocationStoreNotRevoked, { network: "ropsten" }); + expect(fragment).toStrictEqual([ + { + name: "OpenAttestationRevocationStore", + type: "DOCUMENT_STATUS", + status: "VALID", + data: { + details: [ + { + address: "0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3", + revoked: false, + }, + ], + revokedOnAny: false, + }, + }, + ]); + }); + it("should return a invalid fragment when revocation store is used and the document is not revoked", async () => { + const fragment = await verify(documentRopstenRevocationStoreRevoked, { network: "ropsten" }); + expect(fragment).toStrictEqual([ + { + name: "OpenAttestationRevocationStore", + type: "DOCUMENT_STATUS", + reason: { + code: 1, + codeString: "DOCUMENT_REVOKED", + message: + "Certificate 0x856924fa2cf3374bf64697eb0dcf38d0251ff18aedae2bbc193398e8bb11fbd1 has been revoked under contract 0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3", + }, + status: "INVALID", + data: { + details: [ + { + address: "0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3", + reason: { + code: 1, + codeString: "DOCUMENT_REVOKED", + message: + "Certificate 0x856924fa2cf3374bf64697eb0dcf38d0251ff18aedae2bbc193398e8bb11fbd1 has been revoked under contract 0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3", + }, + revoked: true, + }, + ], + revokedOnAny: true, + }, + }, + ]); + }); + }); +}); diff --git a/src/verifiers/revocationStore/openAttestationRevocationStore.ts b/src/verifiers/revocationStore/openAttestationRevocationStore.ts new file mode 100644 index 00000000..97411baa --- /dev/null +++ b/src/verifiers/revocationStore/openAttestationRevocationStore.ts @@ -0,0 +1,34 @@ +import { getData, utils, v2, WrappedDocument } from "@govtechsg/open-attestation"; +import { VerificationFragmentType, Verifier } from "../../types/core"; +import { OpenAttestationRevocationCode } from "../../types/error"; +import { openAttestationEthereumDocumentStoreRevoked } from "../documentStoreRevoked/openAttestationEthereumDocumentStoreRevoked"; + +const name = "OpenAttestationRevocationStore"; +const type: VerificationFragmentType = "DOCUMENT_STATUS"; +export const openAttestationRevocationStore: Verifier> = { + skip: () => { + return Promise.resolve({ + status: "SKIPPED", + type, + name, + reason: { + code: OpenAttestationRevocationCode.SKIPPED, + codeString: OpenAttestationRevocationCode[OpenAttestationRevocationCode.SKIPPED], + message: `Document issuers doesn't have "revocationStore"`, + }, + }); + }, + test: (document) => { + if (utils.isWrappedV2Document(document)) { + const documentData = getData(document); + return documentData.issuers.every((issuer) => "revocationStore" in issuer); + } + return false; + }, + verify: async (document, options) => { + return { + ...(await openAttestationEthereumDocumentStoreRevoked.verify(document, options)), + name, + }; + }, +}; diff --git a/src/verifiers/revocationStore/revocationStore.ts b/src/verifiers/revocationStore/revocationStore.ts new file mode 100644 index 00000000..71f240c7 --- /dev/null +++ b/src/verifiers/revocationStore/revocationStore.ts @@ -0,0 +1,51 @@ +import { SchemaId, v2, WrappedDocument } from "@govtechsg/open-attestation"; + +export const documentRopstenRevocationStoreNotRevoked: WrappedDocument = { + version: SchemaId.v2, + data: { + issuers: [ + { + name: "293d83b9-ffab-4888-b645-a294dce1a9f6:string:John", + identityProof: { + type: "846b133e-25af-4b3c-8464-088dcf0bd7f9:string:DNS-TXT", + location: "e5cf69ff-d95e-409f-a775-2a63694de710:string:tradetrust.io", + }, + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + revocationStore: "79e3f87d-704e-486f-8b01-3c7a04d47896:string:0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3", + }, + ], + foo: "6ccebe9c-7170-4d12-a2b4-34eef1d182d3:string:bar", + }, + signature: { + type: "SHA3MerkleProof", + targetHash: "f5b228fc992405bdb96709f5a141678d8ca39576e211a3624807451039451af0", + proof: [], + merkleRoot: "f5b228fc992405bdb96709f5a141678d8ca39576e211a3624807451039451af0", + }, +}; + +export const documentRopstenRevocationStoreRevoked: WrappedDocument = { + version: SchemaId.v2, + data: { + issuers: [ + { + name: "ca3d28a9-0bc5-4ec5-a020-3a42ba43146a:string:John", + identityProof: { + type: "0bf12863-5868-46f9-ab95-85a85d90c4c7:string:DNS-TXT", + location: "9fb9839a-c263-4976-b3dd-898495187ade:string:tradetrust.io", + }, + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + revocationStore: "3d3766c0-b9bd-4a8f-8612-62e41887374f:string:0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3", + }, + ], + foo: "95b7ea29-e768-4462-be9a-8464de48f55d:string:bar", + }, + signature: { + type: "SHA3MerkleProof", + targetHash: "856924fa2cf3374bf64697eb0dcf38d0251ff18aedae2bbc193398e8bb11fbd1", + proof: [], + merkleRoot: "856924fa2cf3374bf64697eb0dcf38d0251ff18aedae2bbc193398e8bb11fbd1", + }, +};