diff --git a/scripts/generateV4JsonSchemas.ts b/scripts/generateV4JsonSchemas.ts index bae336fb..93cb144b 100644 --- a/scripts/generateV4JsonSchemas.ts +++ b/scripts/generateV4JsonSchemas.ts @@ -1,7 +1,7 @@ import fs from "fs"; import path from "path"; import { zodToJsonSchema } from "zod-to-json-schema"; -import { V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "../src/4.0/types"; +import { V4OpenAttestationDocument, V4WrappedDocument, V4SignedWrappedDocument } from "../src/4.0/types"; const OUTPUT_DIR = path.resolve("./src/4.0/jsonSchemas/__generated__"); @@ -15,7 +15,7 @@ const ZOD_SCHEMAS = [ { filename: "v4-document.schema.json", schemaName: "v4Document", - zodSchema: V4Document, + zodSchema: V4OpenAttestationDocument, }, { filename: "v4-wrapped-document.schema.json", diff --git a/src/4.0/__tests__/e2e.test.ts b/src/4.0/__tests__/e2e.test.ts index b6e6835a..12e5e17e 100644 --- a/src/4.0/__tests__/e2e.test.ts +++ b/src/4.0/__tests__/e2e.test.ts @@ -1,7 +1,7 @@ import { obfuscate, validateSchema, verifySignature } from "../.."; import { cloneDeep, omit } from "lodash"; import { RAW_DOCUMENT_DID, SIGNED_WRAPPED_DOCUMENT_DID, WRAPPED_DOCUMENT_DID } from "../fixtures"; -import { V4Document } from "../types"; +import { V4OpenAttestationDocument } from "../types"; import { wrapDocument, wrapDocuments } from "../wrap"; const DOCUMENT_ONE = { @@ -10,7 +10,7 @@ const DOCUMENT_ONE = { ...RAW_DOCUMENT_DID.credentialSubject, key1: "test", }, -} satisfies V4Document; +} satisfies V4OpenAttestationDocument; const DOCUMENT_TWO = { ...RAW_DOCUMENT_DID, credentialSubject: { @@ -18,7 +18,7 @@ const DOCUMENT_TWO = { key1: "hello", key2: "item2", }, -} satisfies V4Document; +} satisfies V4OpenAttestationDocument; const DOCUMENT_THREE = { ...RAW_DOCUMENT_DID, @@ -29,7 +29,7 @@ const DOCUMENT_THREE = { key3: 3.14159, key4: false, }, -} satisfies V4Document; +} satisfies V4OpenAttestationDocument; const DOCUMENT_FOUR = { ...RAW_DOCUMENT_DID, @@ -38,7 +38,7 @@ const DOCUMENT_FOUR = { key1: "item2", }, }; -const DATUM = [DOCUMENT_ONE, DOCUMENT_TWO, DOCUMENT_THREE, DOCUMENT_FOUR] satisfies V4Document[]; +const DATUM = [DOCUMENT_ONE, DOCUMENT_TWO, DOCUMENT_THREE, DOCUMENT_FOUR] satisfies V4OpenAttestationDocument[]; describe("V4 E2E Test Scenarios", () => { describe("Issuing a single document", () => { @@ -46,7 +46,8 @@ describe("V4 E2E Test Scenarios", () => { const missingData = { ...omit(cloneDeep(DOCUMENT_ONE), "issuer"), }; - await expect(wrapDocument(missingData as unknown as V4Document)).rejects.toThrowErrorMatchingInlineSnapshot(` + await expect(wrapDocument(missingData as unknown as V4OpenAttestationDocument)).rejects + .toThrowErrorMatchingInlineSnapshot(` "Input document does not conform to Open Attestation v4.0 Data Model: { "_errors": [], @@ -113,7 +114,7 @@ describe("V4 E2E Test Scenarios", () => { ...DATUM, { laurent: "task force, assemble!!", - } as unknown as V4Document, + } as unknown as V4OpenAttestationDocument, ]; await expect(wrapDocuments(malformedDatum)).rejects.toThrow( "Input document does not conform to Verifiable Credentials" @@ -171,7 +172,7 @@ describe("V4 E2E Test Scenarios", () => { const credential = { ...RAW_DOCUMENT_DID, issuer: modifiedIssuer, - } satisfies V4Document; + } satisfies V4OpenAttestationDocument; expect(validateSchema(credential)).toStrictEqual(false); }); diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts index f966d4eb..f7dcee28 100644 --- a/src/4.0/__tests__/guard.test.ts +++ b/src/4.0/__tests__/guard.test.ts @@ -1,7 +1,12 @@ import { SUPPORTED_SIGNING_ALGORITHM } from "../../shared/@types/sign"; import { RAW_DOCUMENT_DID } from "../fixtures"; import { signDocument } from "../sign"; -import { W3cVerifiableCredential, V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "../types"; +import { + W3cVerifiableCredential, + V4OpenAttestationDocument, + V4WrappedDocument, + V4SignedWrappedDocument, +} from "../types"; import { wrapDocument } from "../wrap"; const RAW_DOCUMENT = { @@ -16,7 +21,7 @@ const RAW_DOCUMENT = { }, ], }, -} satisfies V4Document; +} satisfies V4OpenAttestationDocument; describe("V4.0 guard", () => { let WRAPPED_DOCUMENT: V4WrappedDocument; @@ -41,7 +46,7 @@ describe("V4.0 guard", () => { }); test("should pass document validation without removal of any data", () => { - const results = V4Document.parse(RAW_DOCUMENT_DID); + const results = V4OpenAttestationDocument.parse(RAW_DOCUMENT_DID); expect(results).toEqual(RAW_DOCUMENT_DID); }); @@ -90,8 +95,8 @@ describe("V4.0 guard", () => { }); test("should pass document validation without removal of any data", () => { - const v4Document: V4Document = WRAPPED_DOCUMENT; - const results = V4Document.parse(v4Document); + const v4Document: V4OpenAttestationDocument = WRAPPED_DOCUMENT; + const results = V4OpenAttestationDocument.parse(v4Document); expect(results).toEqual(WRAPPED_DOCUMENT); }); @@ -138,8 +143,8 @@ describe("V4.0 guard", () => { }); test("should pass document validation without removal of any data", () => { - const v4Document: V4Document = SIGNED_WRAPPED_DOCUMENT; - const results = V4Document.parse(v4Document); + const v4Document: V4OpenAttestationDocument = SIGNED_WRAPPED_DOCUMENT; + const results = V4OpenAttestationDocument.parse(v4Document); expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT); }); diff --git a/src/4.0/__tests__/obfuscate.test.ts b/src/4.0/__tests__/obfuscate.test.ts index b6d68f31..079db9e6 100644 --- a/src/4.0/__tests__/obfuscate.test.ts +++ b/src/4.0/__tests__/obfuscate.test.ts @@ -3,17 +3,17 @@ import { obfuscateVerifiableCredential } from "../obfuscate"; import { get } from "lodash"; import { decodeSalt } from "../salt"; import { wrapDocument } from "../wrap"; -import { Salt, V4Document, V4WrappedDocument } from "../types"; +import { Salt, V4OpenAttestationDocument, V4WrappedDocument } from "../types"; import { verifySignature } from "../../"; import { RAW_DOCUMENT_DID, SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED, WRAPPED_DOCUMENT_DID } from "../fixtures"; import { hashLeafNode } from "../digest"; import { getObfuscatedData, isObfuscated } from "../../shared/utils"; -const makeV4RawDocument = >(props: T) => +const makeV4RawDocument = >(props: T) => ({ ...RAW_DOCUMENT_DID, ...(props as T), - } satisfies V4Document); + } satisfies V4OpenAttestationDocument); const findSaltByPath = (salts: string, path: string): Salt | undefined => { return decodeSalt(salts).find((salt) => salt.path === path); diff --git a/src/4.0/__tests__/wrap.test.ts b/src/4.0/__tests__/wrap.test.ts index c8eeace1..ebd628c6 100644 --- a/src/4.0/__tests__/wrap.test.ts +++ b/src/4.0/__tests__/wrap.test.ts @@ -1,4 +1,4 @@ -import { V4Document, V4WrappedDocument, W3cVerifiableCredential } from "../types"; +import { V4OpenAttestationDocument, V4WrappedDocument, W3cVerifiableCredential } from "../types"; import { wrapDocument } from "../wrap"; describe("V4.0 wrap document", () => { @@ -53,7 +53,7 @@ describe("V4.0 wrap document", () => { id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, - } as V4Document["issuer"], + } as V4OpenAttestationDocument["issuer"], }) ).rejects.toThrowErrorMatchingInlineSnapshot(` "Input document does not conform to Open Attestation v4.0 Data Model: @@ -94,7 +94,7 @@ describe("V4.0 wrap document", () => { }, // this should not exist extraField: "extra", - } as V4Document) + } as V4OpenAttestationDocument) ).rejects.toThrowErrorMatchingInlineSnapshot(` "Input document does not conform to Open Attestation v4.0 Data Model: { @@ -118,7 +118,7 @@ describe("V4.0 wrap document", () => { id: "https://example.com/issuer/123", }, }; - const wrapped = await wrapDocument(genericW3cVc as unknown as V4Document); + const wrapped = await wrapDocument(genericW3cVc as unknown as V4OpenAttestationDocument); const parsedResults = V4WrappedDocument.pick({ "@context": true, type: true }).passthrough().safeParse(wrapped); expect(parsedResults.success).toBe(true); expect(wrapped.proof.merkleRoot.length).toBe(64); diff --git a/src/4.0/diagnose.ts b/src/4.0/diagnose.ts index 99980de5..0a17a8f3 100644 --- a/src/4.0/diagnose.ts +++ b/src/4.0/diagnose.ts @@ -1,10 +1,11 @@ import type { Diagnose } from "../shared/utils/@types/diagnose"; -import { V4WrappedDocument, V4SignedWrappedDocument, V4Document } from "./types"; +import { V4WrappedDocument, V4SignedWrappedDocument, V4OpenAttestationDocument } from "./types"; export const v4Diagnose: Diagnose = ({ document, kind, debug }) => { - let Validator: typeof V4Document | typeof V4WrappedDocument | typeof V4SignedWrappedDocument = V4Document; + let Validator: typeof V4OpenAttestationDocument | typeof V4WrappedDocument | typeof V4SignedWrappedDocument = + V4OpenAttestationDocument; if (kind === "raw") { - Validator = V4Document; + Validator = V4OpenAttestationDocument; } else if (kind === "wrapped") { Validator = V4WrappedDocument; } else { diff --git a/src/4.0/documentBuilder.ts b/src/4.0/documentBuilder.ts index 8d921f3e..0fa8d348 100644 --- a/src/4.0/documentBuilder.ts +++ b/src/4.0/documentBuilder.ts @@ -6,7 +6,7 @@ import { SvgRenderer, OscpResponderRevocation, RevocationStoreRevocation, - V4Document, + V4OpenAttestationDocument, V4SignedWrappedDocument, V4WrappedDocument, } from "./types"; @@ -14,8 +14,8 @@ import { import { ZodError, z } from "zod"; const SingleDocumentProps = z.object({ - name: V4Document.shape.name.unwrap(), - credentialSubject: V4Document.shape.credentialSubject, + name: V4OpenAttestationDocument.shape.name.unwrap(), + credentialSubject: V4OpenAttestationDocument.shape.credentialSubject, }); const DocumentProps = z.union([SingleDocumentProps, z.array(SingleDocumentProps)]); @@ -46,9 +46,9 @@ const SvgRendererProps = z.discriminatedUnion("type", [ ]); const DnsTextIssuanceProps = z.object({ - issuerId: V4Document.shape.issuer.shape.id, - issuerName: V4Document.shape.issuer.shape.name, - identityProofDomain: V4Document.shape.issuer.shape.identityProof.shape.identifier, + issuerId: V4OpenAttestationDocument.shape.issuer.shape.id, + issuerName: V4OpenAttestationDocument.shape.issuer.shape.name, + identityProofDomain: V4OpenAttestationDocument.shape.issuer.shape.identityProof.shape.identifier, }); /** @@ -83,14 +83,14 @@ type DocumentProps = { * * Maps to "credentialSubject" */ - credentialSubject: z.infer; + credentialSubject: z.infer; }; type State = { documentMainProps: DocumentProps | DocumentProps[]; - renderMethod: V4Document["renderMethod"]; - issuer: V4Document["issuer"] | undefined; - credentialStatus: V4Document["credentialStatus"]; + renderMethod: V4OpenAttestationDocument["renderMethod"]; + issuer: V4OpenAttestationDocument["issuer"] | undefined; + credentialStatus: V4OpenAttestationDocument["credentialStatus"]; }; /** @@ -141,7 +141,7 @@ export class DocumentBuilder { credentialSubject, ...(renderMethod && { renderMethod }), ...(credentialStatus && { credentialStatus }), - } satisfies V4Document) + } satisfies V4OpenAttestationDocument) ); return wrapDocuments(toWrap) as unknown as WrappedReturn; @@ -239,7 +239,7 @@ export class DocumentBuilder { identityProofType: "DNS-TXT", identifier: identityProofDomain, }, - } satisfies V4Document["issuer"]; + } satisfies V4OpenAttestationDocument["issuer"]; this.setState("issuer", issuer); return { @@ -287,7 +287,7 @@ export class DocumentBuilder { const credentialStatus = { id: oscpUrl, type: "OpenAttestationOcspResponder", - } satisfies V4Document["credentialStatus"]; + } satisfies V4OpenAttestationDocument["credentialStatus"]; this.setState("credentialStatus", credentialStatus); return this.ISSUANCE_METHODS; @@ -312,7 +312,7 @@ export class DocumentBuilder { const credentialStatus = { id: storeAddress, type: "OpenAttestationRevocationStore", - } satisfies V4Document["credentialStatus"]; + } satisfies V4OpenAttestationDocument["credentialStatus"]; this.setState("credentialStatus", credentialStatus); @@ -350,7 +350,7 @@ export class DocumentBuilder { type: "OpenAttestationEmbeddedRenderer", templateName, }, - ] satisfies V4Document["renderMethod"]; + ] satisfies V4OpenAttestationDocument["renderMethod"]; this.setState("renderMethod", renderMethod); return this.REVOCATION_METHODS; @@ -408,7 +408,7 @@ export class DocumentBuilder { type: "SvgRenderingTemplate2023" as const, digestMultibase: parsedResults.data.digestMultibase, }, - ] satisfies V4Document["renderMethod"]); + ] satisfies V4OpenAttestationDocument["renderMethod"]); this.setState("renderMethod", renderMethod); return this.REVOCATION_METHODS; diff --git a/src/4.0/exports/types.ts b/src/4.0/exports/types.ts index 39e88e96..70c99577 100644 --- a/src/4.0/exports/types.ts +++ b/src/4.0/exports/types.ts @@ -1,5 +1,5 @@ export type { - V4Document as Document, + V4OpenAttestationDocument as OpenAttestationDocument, V4WrappedDocument as WrappedDocument, V4SignedWrappedDocument as SignedWrappedDocument, } from "../types"; diff --git a/src/4.0/exports/utils.ts b/src/4.0/exports/utils.ts index a8a8837b..9e28fa7a 100644 --- a/src/4.0/exports/utils.ts +++ b/src/4.0/exports/utils.ts @@ -1,6 +1,6 @@ export { v4Diagnose as diagnose } from "../diagnose"; export { - isV4Document as isDocument, + isV4OpenAttestationDocument as isOpenAttestationDocument, isV4WrappedDocument as isWrappedDocument, isV4SignedWrappedDocument as isSignedWrappedDocument, } from "../types"; diff --git a/src/4.0/fixtures.ts b/src/4.0/fixtures.ts index d904aca7..17d2082f 100644 --- a/src/4.0/fixtures.ts +++ b/src/4.0/fixtures.ts @@ -1,4 +1,4 @@ -import { V4Document, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; +import { V4OpenAttestationDocument, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; const ISSUER_ID = "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89" as const; export const SAMPLE_SIGNING_KEYS = { @@ -44,7 +44,7 @@ export const RAW_DOCUMENT_DID = freezeObject({ }, ], }, -} satisfies V4Document); +} satisfies V4OpenAttestationDocument); export const RAW_DOCUMENT_DID_OSCP = freezeObject({ "@context": [ @@ -88,7 +88,7 @@ export const RAW_DOCUMENT_DID_OSCP = freezeObject({ }, ], }, -} satisfies V4Document); +} satisfies V4OpenAttestationDocument); export const BATCHED_RAW_DOCUMENTS_DID = freezeObject([ { @@ -170,7 +170,7 @@ export const BATCHED_RAW_DOCUMENTS_DID = freezeObject([ }, ], }, -] satisfies V4Document[]); +] satisfies V4OpenAttestationDocument[]); export const WRAPPED_DOCUMENT_DID = freezeObject({ "@context": [ diff --git a/src/4.0/sign.ts b/src/4.0/sign.ts index db0f8cb1..93c69d9e 100644 --- a/src/4.0/sign.ts +++ b/src/4.0/sign.ts @@ -1,10 +1,10 @@ import { sign } from "../shared/signer"; import { SigningKey } from "../shared/@types/sign"; import { ethers } from "ethers"; -import { V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "./types"; +import { V4OpenAttestationDocument, V4WrappedDocument, V4SignedWrappedDocument } from "./types"; import type { ZodError } from "zod"; -export const signDocument = async ( +export const signDocument = async ( document: V4SignedWrappedDocument | V4WrappedDocument, algorithm: "Secp256k1VerificationKey2018", keyOrSigner: SigningKey | ethers.Signer diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 5db91a00..5fc3ed2b 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -198,7 +198,7 @@ export const RevocationStoreRevocation = z.object({ type: z.literal("OpenAttestationRevocationStore"), }); -export const V4Document = _W3cVerifiableCredential +export const V4OpenAttestationDocument = _W3cVerifiableCredential .extend({ "@context": z @@ -252,26 +252,29 @@ const WrappedProof = z.object({ }); const WrappedDocumentExtrasShape = { proof: WrappedProof.passthrough() } as const; // V4WrappedDocument should never allow extra root properties -export const V4WrappedDocument = V4Document.extend(WrappedDocumentExtrasShape).strict(); +export const V4WrappedDocument = V4OpenAttestationDocument.extend(WrappedDocumentExtrasShape).strict(); const SignedWrappedProof = WrappedProof.extend({ key: z.string(), signature: z.string() }); const SignedWrappedDocumentExtrasShape = { proof: SignedWrappedProof } as const; // V4SignedWrappedDocument should never allow extra root properties -export const V4SignedWrappedDocument = V4Document.extend(SignedWrappedDocumentExtrasShape).strict(); +export const V4SignedWrappedDocument = V4OpenAttestationDocument.extend(SignedWrappedDocumentExtrasShape).strict(); export type W3cVerifiableCredential = z.infer; // AssertStricterOrEqual is used to ensure that we have zod extended from the base type while // still being assignable to the base type. For example, if we accidentally extend and // replaced '@context' to a boolean, this would fail the assertion. -export type V4Document = AssertStricterOrEqual>; +export type V4OpenAttestationDocument = AssertStricterOrEqual< + W3cVerifiableCredential, + z.infer +>; -export type V4WrappedDocument = Override< +export type V4WrappedDocument = Override< T, Pick, keyof typeof WrappedDocumentExtrasShape> >; -export type V4SignedWrappedDocument = Override< +export type V4SignedWrappedDocument = Override< T, Pick, keyof typeof SignedWrappedDocumentExtrasShape> >; @@ -313,8 +316,8 @@ export type PartialDeep = T extends string | number | bigint | boolean | null [K in keyof T]?: PartialDeep; }; -export const isV4Document = (document: unknown): document is V4Document => { - return V4Document.safeParse(document).success; +export const isV4OpenAttestationDocument = (document: unknown): document is V4OpenAttestationDocument => { + return V4OpenAttestationDocument.safeParse(document).success; }; export const isV4WrappedDocument = (document: unknown): document is V4WrappedDocument => { diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 0aa12f57..407b1627 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -1,20 +1,20 @@ import { hashToBuffer } from "../shared/utils/hashing"; import { MerkleTree } from "../shared/merkle"; import { ContextUrl, ContextType, UnableToInterpretContextError, interpretContexts } from "./context"; -import { NoExtraProperties, V4Document, V4WrappedDocument, W3cVerifiableCredential } from "./types"; +import { NoExtraProperties, V4OpenAttestationDocument, V4WrappedDocument, W3cVerifiableCredential } from "./types"; import { digestCredential } from "../4.0/digest"; import { encodeSalt, salt } from "./salt"; import { ZodError } from "zod"; -export const wrapDocument = async ( +export const wrapDocument = async ( // NoExtraProperties prevents the user from passing in a document with extra properties, which is more aligned to our validation strategy of strict - document: NoExtraProperties + document: NoExtraProperties ): Promise> => { /* 1a. try OpenAttestation VC validation, since most user will be issuing oa v4*/ - const oav4context = await V4Document.pick({ "@context": true }).passthrough().safeParseAsync(document); // Superficial check on user intention + const oav4context = await V4OpenAttestationDocument.pick({ "@context": true }).passthrough().safeParseAsync(document); // Superficial check on user intention let validatedRawDocument: W3cVerifiableCredential | undefined; if (oav4context.success) { - const oav4 = await V4Document.safeParseAsync(document); + const oav4 = await V4OpenAttestationDocument.safeParseAsync(document); if (!oav4.success) { throw new DataModelValidationError("Open Attestation v4.0", oav4.error); } @@ -44,7 +44,7 @@ export const wrapDocument = async ( validatedRawDocument["@context"].forEach((context) => contexts.add(context)); } REQUIRED_CONTEXTS.forEach((c) => contexts.delete(c)); - const finalContexts: V4Document["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; + const finalContexts: V4OpenAttestationDocument["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; /* 4. Type validation */ // Ensure that required types are present and in the correct order @@ -57,11 +57,15 @@ export const wrapDocument = async ( types.forEach((type) => types.add(type)); } REQUIRED_TYPES.forEach((t) => types.delete(t)); - const finalTypes: V4Document["type"] = [...REQUIRED_TYPES, ...Array.from(types)]; + const finalTypes: V4OpenAttestationDocument["type"] = [...REQUIRED_TYPES, ...Array.from(types)]; const documentReadyForWrapping = { ...validatedRawDocument, - ...extractAndAssertAsV4DocumentProps(validatedRawDocument, ["issuer", "credentialStatus", "credentialSubject"]), + ...extractAndAssertAsV4OpenAttestationDocumentProps(validatedRawDocument, [ + "issuer", + "credentialStatus", + "credentialSubject", + ]), "@context": finalContexts, type: finalTypes, } satisfies W3cVerifiableCredential; @@ -93,9 +97,9 @@ export const wrapDocument = async ( return verifiableCredential as V4WrappedDocument; }; -export const wrapDocuments = async ( +export const wrapDocuments = async ( // NoExtraProperties prevents the user from passing in a document with extra properties, which is more aligned to our validation strategy of strict - documents: NoExtraProperties[] + documents: NoExtraProperties[] ): Promise[]> => { // create individual verifiable credential const verifiableCredentials = await Promise.all(documents.map((document) => wrapDocument(document))); @@ -126,9 +130,9 @@ export const wrapDocuments = async ( * that are defined in the original document. For example, if we extract * "a" and "b" from { b: "something" } we should only get { b: "something" } NOT * { a: undefined, b: "something" }. We also assert that the extracted properties - * are of V4Document type. + * are of V4OpenAttestationDocument type. **/ -function extractAndAssertAsV4DocumentProps( +function extractAndAssertAsV4OpenAttestationDocumentProps( original: W3cVerifiableCredential, keys: K[] ) { @@ -136,7 +140,7 @@ function extractAndAssertAsV4DocumentProps { if (keys.includes(k as K)) temp[k] = v; }); - return temp as { [key in K]: V4Document[key] }; + return temp as { [key in K]: V4OpenAttestationDocument[key] }; } class DataModelValidationError extends Error { diff --git a/src/index.ts b/src/index.ts index 4230b4af..46276443 100644 --- a/src/index.ts +++ b/src/index.ts @@ -66,7 +66,7 @@ export const validateSchema = (document: WrappedDocument): boolean => { return validate(document, getSchema(SchemaId.v2)).length === 0; else if (utils.isWrappedV3Document(document) || document?.version === SchemaId.v3) return validate(document, getSchema(SchemaId.v3)).length === 0; - else if (utils.isWrappedV4Document(document)) { + else if (utils.isWrappedV4OpenAttestationDocument(document)) { return v4Diagnose({ document, kind: "wrapped", debug: false, mode: "strict" }).length === 0; } @@ -76,7 +76,7 @@ export const validateSchema = (document: WrappedDocument): boolean => { export function verifySignature>(document: T) { if (utils.isWrappedV2Document(document)) return verify(document); else if (utils.isWrappedV3Document(document)) return verifyV3(document); - else if (utils.isWrappedV4Document(document)) return verifyV4(document); + else if (utils.isWrappedV4OpenAttestationDocument(document)) return verifyV4(document); throw new Error("Unsupported document type: Only OpenAttestation v2, v3 or v4 documents can be signature verified"); } @@ -103,7 +103,7 @@ export function obfuscate( export function obfuscate(document: WrappedDocument, fields: string[] | string) { if (utils.isWrappedV2Document(document)) return obfuscateDocumentV2(document, fields); else if (utils.isWrappedV3Document(document)) return obfuscateVerifiableCredentialV3(document, fields); - else if (utils.isWrappedV4Document(document)) return obfuscateVerifiableCredentialV4(document, fields); + else if (utils.isWrappedV4OpenAttestationDocument(document)) return obfuscateVerifiableCredentialV4(document, fields); throw new Error("Unsupported document type: Only OpenAttestation v2, v3 or v4 documents can be obfuscated"); } diff --git a/src/shared/@types/document.ts b/src/shared/@types/document.ts index e22acc25..5e769103 100644 --- a/src/shared/@types/document.ts +++ b/src/shared/@types/document.ts @@ -11,9 +11,9 @@ import { } from "../../3.0/types"; import { Literal, Static, String } from "runtypes"; import { ethers } from "ethers"; -import { V4Document, V4SignedWrappedDocument, V4WrappedDocument } from "../../4.0/types"; +import { V4OpenAttestationDocument, V4SignedWrappedDocument, V4WrappedDocument } from "../../4.0/types"; -export type OpenAttestationDocument = OpenAttestationDocumentV2 | OpenAttestationDocumentV3 | V4Document; +export type OpenAttestationDocument = OpenAttestationDocumentV2 | OpenAttestationDocumentV3 | V4OpenAttestationDocument; export type WrappedDocument = T extends OpenAttestationDocumentV2 ? WrappedDocumentV2 : T extends OpenAttestationDocumentV3 diff --git a/src/shared/utils/guard.ts b/src/shared/utils/guard.ts index dfe8e136..87af70a6 100644 --- a/src/shared/utils/guard.ts +++ b/src/shared/utils/guard.ts @@ -7,7 +7,7 @@ import { OpenAttestationDocument as OpenAttestationDocumentV3, WrappedDocument as WrappedDocumentV3, } from "../../3.0/types"; -import { V4WrappedDocument, V4SignedWrappedDocument, V4Document } from "../../4.0/types"; +import { V4WrappedDocument, V4SignedWrappedDocument, V4OpenAttestationDocument } from "../../4.0/types"; import { diagnose } from "./diagnose"; import { Mode } from "./@types/diagnose"; @@ -40,10 +40,10 @@ export const isRawV3Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isRawV4Document = ( +export const isRawV4OpenAttestationDocument = ( document: any, { mode }: { mode: Mode } = { mode: "non-strict" } -): document is V4Document => { +): document is V4OpenAttestationDocument => { return diagnose({ version: "4.0", kind: "raw", document, debug: false, mode }).length === 0; }; @@ -76,7 +76,7 @@ export const isWrappedV3Document = ( +export const isWrappedV4OpenAttestationDocument = ( document: unknown, { mode }: { mode: Mode } = { mode: "non-strict" } ): document is V4WrappedDocument => { @@ -112,7 +112,7 @@ export const isSignedWrappedV3Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isSignedWrappedV4Document = ( +export const isSignedWrappedV4OpenAttestationDocument = ( document: unknown, { mode }: { mode: Mode } = { mode: "non-strict" } ): document is T => { diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index 1e5a9880..bd947afa 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -18,8 +18,8 @@ import { isWrappedV2Document, isRawV3Document, isWrappedV3Document, - isRawV4Document, - isWrappedV4Document, + isRawV4OpenAttestationDocument, + isWrappedV4OpenAttestationDocument, } from "./guard"; import { Version } from "./diagnose"; @@ -31,7 +31,7 @@ export function getIssuerAddress(document: any): any { return document.openAttestationMetadata.proof.value; } // TODO: OA v4 proof schema not updated to support document store issuance yet - // else if (isWrappedV4Document(document)) { + // else if (isWrappedV4OpenAttestationDocument(document)) { // return document.proof.? // } throw new Error( @@ -42,7 +42,7 @@ export function getIssuerAddress(document: any): any { export const getMerkleRoot = (document: any): string => { if (isWrappedV2Document(document)) return document.signature.merkleRoot; else if (isWrappedV3Document(document)) return document.proof.merkleRoot; - else if (isWrappedV4Document(document)) return document.proof.merkleRoot; + else if (isWrappedV4OpenAttestationDocument(document)) return document.proof.merkleRoot; throw new Error( "Unsupported document type: Only can retrieve merkle root from wrapped OpenAttestation v2, v3 & v4 documents." @@ -52,7 +52,7 @@ export const getMerkleRoot = (document: any): string => { export const getTargetHash = (document: any): string => { if (isWrappedV2Document(document)) return document.signature.targetHash; else if (isWrappedV3Document(document)) return document.proof.targetHash; - else if (isWrappedV4Document(document)) return document.proof.targetHash; + else if (isWrappedV4OpenAttestationDocument(document)) return document.proof.targetHash; throw new Error( "Unsupported document type: Only can retrieve target hash from wrapped OpenAttestation v2, v3 & v4 documents." @@ -70,7 +70,7 @@ export const getTemplateURL = (document: any): string | undefined => { else return document.$template?.url; } else if (isRawV3Document(document) || isWrappedV3Document(document)) { return document.openAttestationMetadata.template?.url; - } else if (isRawV4Document(document) || isWrappedV4Document(document)) { + } else if (isRawV4OpenAttestationDocument(document) || isWrappedV4OpenAttestationDocument(document)) { return document.renderMethod && document.renderMethod[0].id; } @@ -80,7 +80,7 @@ export const getTemplateURL = (document: any): string | undefined => { }; export const getDocumentData = (document: WrappedDocument): OpenAttestationDocument => { - if (isWrappedV3Document(document) || isWrappedV4Document(document)) { + if (isWrappedV3Document(document) || isWrappedV4OpenAttestationDocument(document)) { const omit = (keys: any, obj: any): any => Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k))); return omit(["proof"], document); @@ -123,7 +123,7 @@ export const isDocumentRevokable = (document: any): boolean => { !!document.openAttestationMetadata.proof.value; return isDocumentStoreRevokableV3 || isDidRevokableV3; - } else if (isWrappedV4Document(document)) { + } else if (isWrappedV4OpenAttestationDocument(document)) { if (typeof document.issuer === "string" || !document.credentialStatus) return false; const isDidRevokableV4 = document.issuer.identityProof?.identityProofType === "DNS-DID" @@ -170,7 +170,7 @@ export const isObfuscated = ( return !!document.privacy?.obfuscatedData?.length; } else if (isWrappedV3Document(document)) { return !!document.proof.privacy.obfuscated.length; - } else if (isWrappedV4Document(document)) { + } else if (isWrappedV4OpenAttestationDocument(document)) { return !!document.proof.privacy.obfuscated.length; } @@ -189,7 +189,7 @@ export const getObfuscatedData = ( return document.privacy?.obfuscatedData || []; } else if (isWrappedV3Document(document)) { return document.proof.privacy.obfuscated || []; - } else if (isWrappedV4Document(document)) { + } else if (isWrappedV4OpenAttestationDocument(document)) { return document.proof.privacy.obfuscated || []; } diff --git a/src/shared/validate/validate.ts b/src/shared/validate/validate.ts index 38bc26d1..badb007f 100644 --- a/src/shared/validate/validate.ts +++ b/src/shared/validate/validate.ts @@ -13,7 +13,7 @@ export const validateSchema = (document: any, validator: ValidateFunction, kind? throw new Error("No schema validator provided"); } - // FIXME: Unable to use isWrappedV4Document() type guard here because it also calls validateSchema (endless recursive call) + // FIXME: Unable to use isWrappedV4OpenAttestationDocument() type guard here because it also calls validateSchema (endless recursive call) // Need a better way to determine whether a document needs to be unwrapped first const valid = validator( (Array.isArray(document["@context"]) && document["@context"].includes(ContextUrl.v2_vc)) ||