From 302e61f2138e2023bc78763be1290d2685b8b5ee Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 20 May 2024 13:46:33 +0800 Subject: [PATCH] feat: new readme for 4.0 (#284) * feat: new readme for 4.0 * fix: improved wording * fix: based on kyles suggestions * fix: remove random new line * fix: next.js * fix: introduce lower level fns first, illustrate mapping of higher level fns * feat: added computeDigestMultibase * fix: header * fix: update to reflect code changes * fix: package exports really helps most when treeshaking is not possible --- README.md | 433 +++++++++++++++++++++++++++++++++++----------------- previous.md | 217 ++++++++++++++++++++++++++ 2 files changed, 506 insertions(+), 144 deletions(-) create mode 100644 previous.md diff --git a/README.md b/README.md index d324571c..64a7da1f 100644 --- a/README.md +++ b/README.md @@ -1,217 +1,362 @@ # OpenAttestation -OpenAttestation is a framework of attestation and notary for any document types on the blockchain. +OpenAttestation is an open source document endorsement and verification framework for verifiable documents and transferable records. Documents issued this way are cryptographically trustworthy and can be verified independently. With OpenAttestation, you can issue and verify verifiable documents individually or in a batch using: -* either smart contracts on the Ethereum blockchain +- either smart contracts on the Ethereum blockchain +- or digital signatures without the need to pay for Ethereum transactions -* or digital signatures without the need to pay for Ethereum transactions +## Note -## Installation +This guide concentrates on OpenAttestation v4.0 documents. For issuing v2.0 documents, please refer to the [previous version of the documentation](previous.md) -To install the OpenAttestation framework on your machine, run the command below: +## Requirements -```bash -npm i @govtechsg/open-attestation -``` +### Node.js ---- +Version 12 or higher is required. -## Usage +#### Frontend Frameworks/Toolings -### Wrapping documents +##### CRA (Create React App) -The `wrapDocuments` function takes in an array of documents and returns the wrapped batch. Each document must be valid according to the [version of the schema used](#version-of-the-schema). It computes the merkle root of the batch and appends it to each document. This merkle root can be published on the blockchain and be queried against to prove the provenance of the document issued this way. Alternatively, the merkle root may be signed by the document issuer's private key, which may be cryptographically verified using the issuer's public key or Ethereum account to act as proof of issuance. +Compatible out of the box. -#### Version of the schema -In future, this function may accept a second optional parameter to specify the version of open-attestation you want to use. Currently, open-attestation will use schema 2.0. See [Additional Information](#additional-information) for how to use experimental v3.0 documents, which aim to be compatible with the W3C's data model for [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/). +##### Vite -#### Code example +``` +// vite.config.ts +export default defineConfig({ + ... + define: { + global: 'globalThis' + }, +}) +``` -The `wrapDocument` function is almost identical with `wrapDocuments`, but the first function accepts only one document. +##### Next.js -The following code example shows how `wrapDocuments` works. You can also do some tweaking to apply it on `wrapDocument`. +- Client-side usage: -```js -import { wrapDocuments } from "@govtechsg/open-attestation"; -const document = { - id: "SERIAL_NUMBER_123", - $template: { - name: "CUSTOM_TEMPLATE", - type: "EMBEDDED_RENDERER", - url: "https://localhost:3000/renderer", - }, - issuers: [ - { - name: "DEMO STORE", - tokenRegistry: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", - identityProof: { - type: "DNS-TXT", - location: "tradetrust.io", - }, - }, - ], - recipient: { - name: "Recipient Name", - }, - unknownKey: "unknownValue", - attachments: [ - { - filename: "sample.pdf", - type: "application/pdf", - data: "BASE64_ENCODED_FILE", - }, - ], -}; - -wrappedDocuments = wrapDocuments([document, { ...document, id: "different id" }]); // will ensure document is valid regarding open-attestation 2.0 schema -console.log(wrappedDocuments); +``` +npm i undici ``` -#### Differences between the two similar functions +- Server-side usage should function out of the box. -Although `wrapDocument` and `wrapDocuments` are almost identical, there are minor differences. +## Installation -- The `wrapDocuments` function returns an array and not an object. -- Each element in the array is a wrapped document corresponding to the one provided as input. -- Each element will share the same unique `merkleRoot` value in every batch wrap instance. -- Each element has a unique `targetHash` value. -- Similar to `wrapDocument`, every time you run `wrapDocuments`, it will create unique hashes (in front of every field in the data object). +### Via npm -### Signing a document +```bash +npm i @govtechsg/open-attestation +``` + +--- -The `signDocument` function takes a wrapped document, as well as a public/private key pair or an [Ethers.js Signer](https://docs.ethers.io/v5/api/signer/). The method will sign the merkle root from the wrapped document, append the signature to the document, and return it. Currently, it supports the signing algorithm below: +## Basic Usage -- `Secp256k1VerificationKey2018` +### Document creation -#### Example with a public/private key pair +#### Option 1. with `wrapDocument`, `wrapDocuments` and `signDocument` -The following code example of `signDocument` contains a public/private key pair. +```typescript +// granular imports are possible too +// import { wrapDocument, wrapDocumentErrors } from "@govtechsg/open-attestation/4.0/wrap"; +// import { signDocument, signDocumentErrors } from "@govtechsg/open-attestation/4.0/sign"; -```js -import { signDocument, SUPPORTED_SIGNING_ALGORITHM } from "@govtechsg/open-attestation"; -await signDocument(wrappedV2Document, SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, { - public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", - private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655", -}); +import { v4 } from "@govtechsg/open-attestation"; +const { wrapDocument, wrapDocumentErrors, signDocument, signDocumentErrors } = v4; + +try { + const wrappedDocument = await wrapDocument({ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + name: "Republic of Singapore Driving Licence", + issuer: { + id: "urn:uuid:344a5b29-30ce-4b8f-a7dc-c1f056c86f5a", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + credentialSubject: { + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + // attachments if any + attachments: [ + { + filename: "sample.pdf", + mimeType: "application/pdf", + data: "base64 encoded data", + }, + ], + }, + credentialStatus: { + type: "OpenAttestationRevocationStore", + id: "0x1234567890123456789012345678901234567890", + }, + }); + + const signedDocument = await signDocument(wrappedDocument, "Secp256k1VerificationKey2018", { + private: "0x1234567890123456789012345678901234567890123456789012345678901234", + public: "0x12345678901234567890123456789012345678901234567890123456789012345678901234", + }); +} catch (error) { + if (error instanceof wrapDocumentErrors.UnableToInterpretContextError) { + // network errors can occur when fetching the context + } else if (error instanceof signDocumentErrors.CouldNotSignDocumentError) { + // network errors can occur when signing using a remote Signer + } + + throw error; +} ``` -#### Example with the signer -The following code example of `signDocument` contains the signer information. +#### Option 2. Using `DocumentBuilder` + +`DocumentBuilder` is a class designed to streamline the document creation process, reducing the likelihood of errors. While it should suffice for most use cases, those needing finer control should use Option 1. + +The following example creates a document with the same content as the previous example: + +```typescript +// granular imports are possible too +// import { DocumentBuilder, DocumentBuilderErrors } from "@govtechsg/open-attestation/4.0/builder"; +import { v4 } from "@govtechsg/open-attestation"; +const { DocumentBuilder, DocumentBuilderErrors } = v4; + +try { + const signedDocument = await new DocumentBuilder({ + // name of the document + name: "Republic of Singapore Driving Licence", + // main content of the document + credentialSubject: { + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + // attachments if any + attachments: [ + { + filename: "sample.pdf", + mimeType: "application/pdf", + data: "base64 encoded data", + }, + ], + }, + }) + // Equivalent to setting "renderMethod" to: + // [ + // { + // id: "https://demo-renderer.opencerts.io", + // type: "OpenAttestationEmbeddedRenderer", + // templateName: "GOVTECH_DEMO", + // }, + // ], + .embeddedRenderer({ + templateName: "GOVTECH_DEMO", + rendererUrl: "https://demo-renderer.opencerts.io", + }) + // Equivalent to setting "credentialStatus" to: + // { + // type: "OpenAttestationRevocationStore", + // id: "0x1234567890123456789012345678901234567890" + // }, + .revocationStoreRevocation({ + storeAddress: "0x1234567890123456789012345678901234567890", + }) + // Equivalent to setting "issuer" to: + // { + // id: "urn:uuid:344a5b29-30ce-4b8f-a7dc-c1f056c86f5a", + // type: "OpenAttestationIssuer", + // name: "Government Technology Agency of Singapore (GovTech)", + // identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + // }, + .dnsTxtIssuance({ + identityProofDomain: "example.openattestation.com", + issuerName: "Government Technology Agency of Singapore (GovTech)", + issuerId: "urn:uuid:344a5b29-30ce-4b8f-a7dc-c1f056c86f5a", + }) + .wrapAndSign({ + signer: { + public: "0x12345678901234567890123456789012345678901234567890123456789012345678901234", + private: "0x1234567890123456789012345678901234567890123456789012345678901234", + }, + }); +} catch (error) { + if (error instanceof DocumentBuilderErrors.CouldNotSignDocumentError) { + // handle error + } +} +``` -```js -import { signDocument, SUPPORTED_SIGNING_ALGORITHM } from "@govtechsg/open-attestation"; -import { Wallet } from "ethers"; +Batch creation is supported, though all documents in the batch must share the same issuer, revocation and rendering method: -const wallet = Wallet.fromMnemonic("tourist quality multiply denial diary height funny calm disease buddy speed gold"); -const { proof } = await signDocument( - wrappedDocumentV2, - SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, - wallet -); +```typescript +const signedDocuments = await new DocumentBuilder([ + { + name: "Republic of Singapore Driving Licence", + credentialSubject: { + ... + }, + }, + { + name: "Republic of Singapore Driving Licence", + credentialSubject: { + ... + }, + }, +]) +... ``` -### Validating the document schema +## Obfuscation -The `validateSchema` function checks that the document conforms to OpenAttestation data structure. +This process enables the concealment of sensitive information within a document without compromising the validity of its signature, useful when sharing with third parties. -```js -import { validateSchema } from "@govtechsg/open-attestation"; -const validatedSchema = validateSchema(wrappedDocument); -console.log(validatedSchema); -``` +It's crucial to understand that obfuscation can only be applied to non-essential data paths. Critical paths, such as issuer and proof, must remain unobscured as they are integral for the verification process. -### Verifying the document signature +### Applicable only to OpenAttestation v4.0 documents -The `verifySignature` function checks that the signature of the document corresponds to the actual content in the document. In addition, it checks that the target hash (hash of the document content) is part of the set of documents wrapped in the batch using the proofs. +```typescript +// granular imports are possible too +// import { obfuscate } from "@govtechsg/open-attestation/4.0/obfuscate"; +import { v4 } from "@govtechsg/open-attestation"; +const { obfuscate } = v4; -```js -import { verifySignature } from "@govtechsg/open-attestation"; -const verified = verifySignature(wrappedDocument); -console.log(verified); +const obfuscatedDocument = obfuscate(signedDocument, ["credentialSubject.name", "credentialSubject.licenses[0].class"]); ``` ->**Note:** This method does not check against the blockchain or any registry if this document has been published. The merkle root of this document needs to be checked against a publicly accessible document store, which in the case of OpenAttestation is a smart contract on the blockchain. +## Verification of Signature -### Retrieving the document data +### Exclusive to OpenAttestation v4.0 documents -The `getData` function returns the original data stored in the document in a readable format. +```typescript +// granular imports are possible too +// import { verifySignature } from "@govtechsg/open-attestation/4.0/verify"; +import { v4 } from "@govtechsg/open-attestation"; +const { verifySignature } = v4; -```js -import { getData } from "@govtechsg/open-attestation"; -const data = getData(wrappedDocument); -console.log(data); +const valid = verifySignature(signedDocument); // true or false ``` -### Utils +### Compatible with any version of OpenAttestation documents -A number of utility functions can be found under `utils`, which check whether a document is a valid OA file that has been wrapped or signed. The `diagnose` function can also be used for troubleshooting. +```typescript +import { verifySignature } from "@govtechsg/open-attestation"; -```js -import { utils } from "@govtechsg/open-attestation"; -utils.isWrappedV3Document(document); +const valid = verifySignature(signedDocument); // true or false ``` -You can replace `isWrappedV3Document` in the above example with the following values: -- `isWrappedV2Document`: This is the type guard for the wrapped V2 document. -- `isSignedWrappedV2Document`: This is the type guard for the signed V2 document. -- `isSignedWrappedV3Document`: This is the type guard for the signed V3 document. -- `isWrappedV3Document`: This is the type guard for the wrapped V3 document. +## Types -In addition, the `diagnose` tool can find out why a document is not a valid OpenAttestation file (wrapped or signed document). +TypeScript types for OpenAttestation v4 documents are included in the package and can be accessed as follows: -### Obfuscating data +```typescript +// granular imports are possible too +// import type { WrappedDocument, SignedWrappedDocument } from "@govtechsg/open-attestation/4.0/types"; +import type { v4 } from "@govtechsg/open-attestation"; +const { WrappedDocument, SignedWrappedDocument } = v4; +``` + +## Utils -The `obfuscateDocument` function removes a key-value pair from the document's data section, without causing the file hash to change. You can use this to generate a new document containing a subset of the original data. +```typescript +// granular imports are possible too +// import { +// isOpenAttestationDocument, +// isWrappedDocument, +// isSignedWrappedDocument, +// computeDigestMultibase, +// } from "@govtechsg/open-attestation/4.0/utils"; -```js -const newData = obfuscateDocument(wrappedDocument, "key1"); -console.log(newData); +import { v4 } from "@govtechsg/open-attestation"; +const { isOpenAttestationDocument, isWrappedDocument, isSignedWrappedDocument, computeDigestMultibase } = v4; ``` -## Development +### `computeDigestMultibase` -To run tests, use the following command: +Function to compute a SHA-256 digest of the provided data and encodes it in Base58 with a 'z' prefix. This is particular useful when computing for the `digestMultibase` of a remote svg template. See [Verifiable Credential Rendering Methods](https://w3c-ccg.github.io/vc-render-method/#svgrenderingtemplate2023). + +```typescript +const svgTemplateResponse = await fetch("https://example.com/svg-template.svg"); +const digestMultibase = await computeDigestMultibase(await svgTemplateResponse.arrayBuffer()); +const wrappedDocument = await wrapDocument( + { + ... + { + id: "https://example.com/svg-template.svg", + type: "SvgRenderingTemplate2023", + digestMultibase: digestMultibase, + } + ... + }) -``` -npm run test ``` -### vc-test-suite +### Guards -You can run `vc-test-suite` against `open-attestation` by using the `npm run test:vc` command. It will: +1. `isOpenAttestationDocument` - Checks if the document is a valid OpenAttestation v4 document +2. `isWrappedDocument` - Checks if the document is a valid OpenAttestation v4 wrapped document +3. `isSignedWrappedDocument` - Checks if the document is a valid OpenAttestation v4 signed wrapped document -- clone https://github.com/w3c/vc-test-suite.git -- copy the local configuration (`vc-test-suite-config.json`) into the cloned repository -- install the latest version of `@govtechsg/open-attestation-cli` -- monkey patch `open-attestation` in `@govtechsg/open-attestation-cli` - - "Monkey patch" means it will build the current version of the project, which will replace the one installed with `@govtechsg/open-attestation-cli`. +### Importing Methods -#### Local debugging +For projects supporting package exports, it is recommended to import the methods as: -If you face a problem with one test and want to debug locally: +```typescript +import { wrapDocument, wrapDocumentErrors } from "@govtechsg/open-attestation/4.0/wrap"; +import { signDocument, signDocumentErrors } from "@govtechsg/open-attestation/4.0/sign"; +import { obfuscate } from "@govtechsg/open-attestation/4.0/obfuscate"; +import { verifySignature } from "@govtechsg/open-attestation/4.0/verify"; +import { isSignedWrappedDocument } from "@govtechsg/open-attestation/4.0/utils"; +``` -1. Ensure the `vc-test-suite` folder is available from the root of the project. +Fallback method for projects not supporting package exports: - If not, run `npm run test:vc` first and perform the test again. +```typescript +import { v4 } from '@govtechsg/open-attestation' +await v4.wrapDocument( + { + ... + } +) +``` -1. Open `runVcTest.sh` and update `install_vc_test_suite=true` to `install_vc_test_suite=false`. +## Development - This line will preserve the `vc-test-suite` folder untouched. +To execute tests: -You can now debug from the `vc-test-suite` folder the way you need. +``` +npm run test +``` ## Additional information - If you find a bug, have a question, or want to share an idea, reach us at our [Github repository](https://github.com/Open-Attestation/open-attestation). -- We are currently building a new version of the schema compatible with W3C VC. This is very experimental and whatever is available for V2 documents are also available for V3 documents: - - [OA schema v3](https://schema.openattestation.com/3.0/schema.json) - - Typings: `import {v3} from "@govtechsg/open-attestation"`. - - Type guard: `utils.isWrappedV3Document`. - - Wrapping: `__unsafe__use__it__at__your__own__risks__wrapDocument` - - future usage: `wrapDocument(document, {version: "open-attestation/3.0"})` - - Example docs in `tests/fixtures/v3` -- There are extra utilities available: - - Refer to the [utils](https://github.com/Open-Attestation/open-attestation/blob/master/src/shared/utils/utils.ts) component for the full list of utilities. diff --git a/previous.md b/previous.md new file mode 100644 index 00000000..d324571c --- /dev/null +++ b/previous.md @@ -0,0 +1,217 @@ +# OpenAttestation + +OpenAttestation is a framework of attestation and notary for any document types on the blockchain. + +With OpenAttestation, you can issue and verify verifiable documents individually or in a batch using: + +* either smart contracts on the Ethereum blockchain + +* or digital signatures without the need to pay for Ethereum transactions + +## Installation + +To install the OpenAttestation framework on your machine, run the command below: + +```bash +npm i @govtechsg/open-attestation +``` + +--- + +## Usage + +### Wrapping documents + +The `wrapDocuments` function takes in an array of documents and returns the wrapped batch. Each document must be valid according to the [version of the schema used](#version-of-the-schema). It computes the merkle root of the batch and appends it to each document. This merkle root can be published on the blockchain and be queried against to prove the provenance of the document issued this way. Alternatively, the merkle root may be signed by the document issuer's private key, which may be cryptographically verified using the issuer's public key or Ethereum account to act as proof of issuance. + +#### Version of the schema +In future, this function may accept a second optional parameter to specify the version of open-attestation you want to use. Currently, open-attestation will use schema 2.0. See [Additional Information](#additional-information) for how to use experimental v3.0 documents, which aim to be compatible with the W3C's data model for [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/). + +#### Code example + +The `wrapDocument` function is almost identical with `wrapDocuments`, but the first function accepts only one document. + +The following code example shows how `wrapDocuments` works. You can also do some tweaking to apply it on `wrapDocument`. + +```js +import { wrapDocuments } from "@govtechsg/open-attestation"; +const document = { + id: "SERIAL_NUMBER_123", + $template: { + name: "CUSTOM_TEMPLATE", + type: "EMBEDDED_RENDERER", + url: "https://localhost:3000/renderer", + }, + issuers: [ + { + name: "DEMO STORE", + tokenRegistry: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", + identityProof: { + type: "DNS-TXT", + location: "tradetrust.io", + }, + }, + ], + recipient: { + name: "Recipient Name", + }, + unknownKey: "unknownValue", + attachments: [ + { + filename: "sample.pdf", + type: "application/pdf", + data: "BASE64_ENCODED_FILE", + }, + ], +}; + +wrappedDocuments = wrapDocuments([document, { ...document, id: "different id" }]); // will ensure document is valid regarding open-attestation 2.0 schema +console.log(wrappedDocuments); +``` + +#### Differences between the two similar functions + +Although `wrapDocument` and `wrapDocuments` are almost identical, there are minor differences. + +- The `wrapDocuments` function returns an array and not an object. +- Each element in the array is a wrapped document corresponding to the one provided as input. +- Each element will share the same unique `merkleRoot` value in every batch wrap instance. +- Each element has a unique `targetHash` value. +- Similar to `wrapDocument`, every time you run `wrapDocuments`, it will create unique hashes (in front of every field in the data object). + +### Signing a document + +The `signDocument` function takes a wrapped document, as well as a public/private key pair or an [Ethers.js Signer](https://docs.ethers.io/v5/api/signer/). The method will sign the merkle root from the wrapped document, append the signature to the document, and return it. Currently, it supports the signing algorithm below: + +- `Secp256k1VerificationKey2018` + +#### Example with a public/private key pair + +The following code example of `signDocument` contains a public/private key pair. + +```js +import { signDocument, SUPPORTED_SIGNING_ALGORITHM } from "@govtechsg/open-attestation"; +await signDocument(wrappedV2Document, SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, { + public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655", +}); +``` + +#### Example with the signer +The following code example of `signDocument` contains the signer information. + +```js +import { signDocument, SUPPORTED_SIGNING_ALGORITHM } from "@govtechsg/open-attestation"; +import { Wallet } from "ethers"; + +const wallet = Wallet.fromMnemonic("tourist quality multiply denial diary height funny calm disease buddy speed gold"); +const { proof } = await signDocument( + wrappedDocumentV2, + SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, + wallet +); +``` + +### Validating the document schema + +The `validateSchema` function checks that the document conforms to OpenAttestation data structure. + +```js +import { validateSchema } from "@govtechsg/open-attestation"; +const validatedSchema = validateSchema(wrappedDocument); +console.log(validatedSchema); +``` + +### Verifying the document signature + +The `verifySignature` function checks that the signature of the document corresponds to the actual content in the document. In addition, it checks that the target hash (hash of the document content) is part of the set of documents wrapped in the batch using the proofs. + +```js +import { verifySignature } from "@govtechsg/open-attestation"; +const verified = verifySignature(wrappedDocument); +console.log(verified); +``` + +>**Note:** This method does not check against the blockchain or any registry if this document has been published. The merkle root of this document needs to be checked against a publicly accessible document store, which in the case of OpenAttestation is a smart contract on the blockchain. + +### Retrieving the document data + +The `getData` function returns the original data stored in the document in a readable format. + +```js +import { getData } from "@govtechsg/open-attestation"; +const data = getData(wrappedDocument); +console.log(data); +``` + +### Utils + +A number of utility functions can be found under `utils`, which check whether a document is a valid OA file that has been wrapped or signed. The `diagnose` function can also be used for troubleshooting. + +```js +import { utils } from "@govtechsg/open-attestation"; +utils.isWrappedV3Document(document); +``` +You can replace `isWrappedV3Document` in the above example with the following values: + +- `isWrappedV2Document`: This is the type guard for the wrapped V2 document. +- `isSignedWrappedV2Document`: This is the type guard for the signed V2 document. +- `isSignedWrappedV3Document`: This is the type guard for the signed V3 document. +- `isWrappedV3Document`: This is the type guard for the wrapped V3 document. + +In addition, the `diagnose` tool can find out why a document is not a valid OpenAttestation file (wrapped or signed document). + +### Obfuscating data + +The `obfuscateDocument` function removes a key-value pair from the document's data section, without causing the file hash to change. You can use this to generate a new document containing a subset of the original data. + +```js +const newData = obfuscateDocument(wrappedDocument, "key1"); +console.log(newData); +``` + +## Development + +To run tests, use the following command: + +``` +npm run test +``` + +### vc-test-suite + +You can run `vc-test-suite` against `open-attestation` by using the `npm run test:vc` command. It will: + +- clone https://github.com/w3c/vc-test-suite.git +- copy the local configuration (`vc-test-suite-config.json`) into the cloned repository +- install the latest version of `@govtechsg/open-attestation-cli` +- monkey patch `open-attestation` in `@govtechsg/open-attestation-cli` + + "Monkey patch" means it will build the current version of the project, which will replace the one installed with `@govtechsg/open-attestation-cli`. + +#### Local debugging + +If you face a problem with one test and want to debug locally: + +1. Ensure the `vc-test-suite` folder is available from the root of the project. + + If not, run `npm run test:vc` first and perform the test again. + +1. Open `runVcTest.sh` and update `install_vc_test_suite=true` to `install_vc_test_suite=false`. + + This line will preserve the `vc-test-suite` folder untouched. + +You can now debug from the `vc-test-suite` folder the way you need. + +## Additional information + +- If you find a bug, have a question, or want to share an idea, reach us at our [Github repository](https://github.com/Open-Attestation/open-attestation). +- We are currently building a new version of the schema compatible with W3C VC. This is very experimental and whatever is available for V2 documents are also available for V3 documents: + - [OA schema v3](https://schema.openattestation.com/3.0/schema.json) + - Typings: `import {v3} from "@govtechsg/open-attestation"`. + - Type guard: `utils.isWrappedV3Document`. + - Wrapping: `__unsafe__use__it__at__your__own__risks__wrapDocument` + - future usage: `wrapDocument(document, {version: "open-attestation/3.0"})` + - Example docs in `tests/fixtures/v3` +- There are extra utilities available: + - Refer to the [utils](https://github.com/Open-Attestation/open-attestation/blob/master/src/shared/utils/utils.ts) component for the full list of utilities.