Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework WASM examples to explicitly use client configs. #275

Merged
merged 3 commits into from May 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 19 additions & 9 deletions bindings/wasm/examples/README.md
Expand Up @@ -4,6 +4,16 @@

This folder provides code examples for you to learn how the IOTA Identity WASM bindings can be used in JavaScript.

If you are writing code against the test network then, most function calls will need to include information about the
network, since this is not automatically inferred from the arguments in all cases currently.

We recommend that you **always** use a `CLIENT_CONFIG` parameter that you define when calling any functions that take a
`ClientConfig` object. This will ensure that all the API calls use a consistent node and network throughout. If you
mismatch the network across calls you will encounter errors.

A `ClientConfig` is a record consisting of two string fields: `network` and `node`. There is an example client config
that can be found in the `config.js` file.

You can run each example using

```
Expand All @@ -18,12 +28,12 @@ npm run example:node -- create_did

The following examples are currently available:

| # | Name | Information |
| :--: | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
| 1 | [create_did](create_did.js) | Generates and publishes a DID Document, the fundamental building block for decentralized identity. |
| 2 | [manipulate_did](manipulate_did.js) | Add verification methods and service endpoints to a DID Document and update an already existing DID Document. |
| 3 | [resolution](resolution.js) | Resolves an existing DID to return the latest DID Document. |
| 4 | [create_vc](create_vc.js) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (VC) specifying claims about the subject, and verifies it. |
| 5 | [revocation](revocation.js) | Remove a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. |
| 6 | [create_vp](create_vp.js) | Create a Verifiable Presentation, the data model for sharing VCs, out of a Verifiable Credential and verifies it. |
| 7 | [merkle_key](merkle_key.js) | Adds a MerkleKeyCollection verification method to an Issuers DID Document and signs a Verifiable Credential with the key on index 0. Afterwards the key on index 0 is deactivated, making the Verifiable Credential fail its verification. |
| # | Name | Information |
| :-: | :---------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | [create_did](create_did.js) | Generates and publishes a DID Document, the fundamental building block for decentralized identity. |
| 2 | [manipulate_did](manipulate_did.js) | Add verification methods and service endpoints to a DID Document and update an already existing DID Document. |
| 3 | [resolution](resolution.js) | Resolves an existing DID to return the latest DID Document. |
| 4 | [create_vc](create_vc.js) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (VC) specifying claims about the subject, and verifies it. |
| 5 | [revocation](revocation.js) | Remove a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. |
| 6 | [create_vp](create_vp.js) | Create a Verifiable Presentation, the data model for sharing VCs, out of a Verifiable Credential and verifies it. |
| 7 | [merkle_key](merkle_key.js) | Adds a MerkleKeyCollection verification method to an Issuers DID Document and signs a Verifiable Credential with the key on index 0. Afterwards the key on index 0 is deactivated, making the Verifiable Credential fail its verification. |
4 changes: 1 addition & 3 deletions bindings/wasm/examples/config.js
@@ -1,9 +1,7 @@
/* @type {{network: string, node: string}} */
const CLIENT_CONFIG = {
network: "main",
node: "https://chrysalis-nodes.iota.org:443",
}

const EXPLORER_URL = "https://explorer.iota.org/mainnet/transaction";

exports.CLIENT_CONFIG = CLIENT_CONFIG;
exports.EXPLORER_URL = EXPLORER_URL;
20 changes: 10 additions & 10 deletions bindings/wasm/examples/create_did.js
Expand Up @@ -2,28 +2,28 @@
// SPDX-License-Identifier: Apache-2.0

const { Document, KeyType, publish } = require('../node/identity_wasm')
const { CLIENT_CONFIG, EXPLORER_URL } = require('./config')
const { logExplorerUrl } = require('./explorer_util')

/*
This example shows a basic introduction on how to create a basic DID Document and upload it to the Tangle.
A ED25519 Keypair is generated, from which the public key is hashed, becoming the DID.
The keypair becomes part of the DID Document in order to prove a link between the DID and the published DID Document.
That same keypair should be used to sign the original DID Document.
*/
async function createIdentity() {
//Create a DID Document (an identity).
const { doc, key } = new Document(KeyType.Ed25519)
async function createIdentity(clientConfig) {
// Create a DID Document (an identity).
const { doc, key } = new Document(KeyType.Ed25519, clientConfig.network);

//Sign the DID Document with the generated key
// Sign the DID Document with the generated key.
doc.sign(key);

//Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
const messageId = await publish(doc.toJSON(), CLIENT_CONFIG);
// Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
const messageId = await publish(doc.toJSON(), clientConfig);

//Log the results
console.log(`Identity Creation: ${EXPLORER_URL}/${messageId}`);
// Log the results.
logExplorerUrl("Identity Creation:", clientConfig.network, messageId);

//Return the results
// Return the results.
return {key, doc, messageId};
}

Expand Down
17 changes: 9 additions & 8 deletions bindings/wasm/examples/create_vc.js
Expand Up @@ -4,18 +4,19 @@
const { VerifiableCredential, checkCredential } = require('../node/identity_wasm')
const { createIdentity } = require('./create_did');
const { manipulateIdentity } = require('./manipulate_did');
const { CLIENT_CONFIG } = require('./config')

/*
This example shows how to create a Verifiable Credential and validate it.
In this example, alice takes the role of the subject, while we also have an issuer.
The issuer signs a UniversityDegreeCredential type verifiable credential with Alice's name and DID.
This Verifiable Credential can be verified by anyone, allowing Alice to take control of it and share it with whoever they please.

@param {{network: string, node: string}} clientConfig
*/
async function createVC() {
//Creates new identities (See "create_did" and "manipulate_did" examples)
const alice = await createIdentity();
const issuer = await manipulateIdentity();
async function createVC(clientConfig) {
// Creates new identities (See "create_did" and "manipulate_did" examples)
const alice = await createIdentity(clientConfig);
const issuer = await manipulateIdentity(clientConfig);

// Prepare a credential subject indicating the degree earned by Alice
let credentialSubject = {
Expand All @@ -34,15 +35,15 @@ async function createVC() {
credentialSubject,
});

//Sign the credential with the Issuer's newKey
// Sign the credential with the Issuer's newKey
const signedVc = issuer.doc.signCredential(unsignedVc, {
method: issuer.doc.id.toString()+"#newKey",
public: issuer.newKey.public,
secret: issuer.newKey.secret,
});

//Check if the credential is verifiable
const result = await checkCredential(signedVc.toString(), CLIENT_CONFIG);
// Check if the credential is verifiable.
const result = await checkCredential(signedVc.toString(), clientConfig);
console.log(`VC verification result: ${result.verified}`);

return {alice, issuer, signedVc};
Expand Down
9 changes: 5 additions & 4 deletions bindings/wasm/examples/create_vp.js
Expand Up @@ -3,16 +3,17 @@

const { VerifiablePresentation, checkPresentation } = require('../node/identity_wasm')
const { createVC } = require('./create_VC');
const { CLIENT_CONFIG } = require('./config')

/*
This example shows how to create a Verifiable Presentation and validate it.
A Verifiable Presentation is the format in which a (collection of) Verifiable Credential(s) gets shared.
It is signed by the subject, to prove control over the Verifiable Credential with a nonce or timestamp.

@param {{network: string, node: string}} clientConfig
*/
async function createVP() {
async function createVP(clientConfig) {
// Creates new identities (See "createVC" example)
const {alice, issuer, signedVc} = await createVC();
const {alice, issuer, signedVc} = await createVC(clientConfig);

// Create a Verifiable Presentation from the Credential - signed by Alice's key
// TODO: Sign with a challenge
Expand All @@ -24,7 +25,7 @@ async function createVP() {
})

// Check the validation status of the Verifiable Presentation
const result = await checkPresentation(signedVp.toString(), CLIENT_CONFIG);
const result = await checkPresentation(signedVp.toString(), clientConfig);
console.log(`VP verification result: ${result.verified}`);
}

Expand Down
15 changes: 15 additions & 0 deletions bindings/wasm/examples/explorer_util.js
@@ -0,0 +1,15 @@
// Copyright 2020-2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

/*
Write out the Targle Explorer URL given the network and message ID, with the given preamble.

@param {!string} preamble
@param {!string} network
@param {!string} messageId
*/
function logExplorerUrl(preamble, network, messageId) {
console.log(`${preamble} https://explorer.iota.org/${network}net/transaction/${messageId}`);
}

exports.logExplorerUrl = logExplorerUrl;
18 changes: 10 additions & 8 deletions bindings/wasm/examples/manipulate_did.js
Expand Up @@ -3,7 +3,7 @@

const { KeyPair, KeyType, publish, VerificationMethod, Service } = require('../node/identity_wasm')
const { createIdentity } = require('./create_did');
const { CLIENT_CONFIG, EXPLORER_URL } = require('./config');
const { logExplorerUrl } = require('./explorer_util');

/*
This example shows how to add more to an existing DID Document.
Expand All @@ -12,10 +12,12 @@ const { CLIENT_CONFIG, EXPLORER_URL } = require('./config');
The services provide metadata around the identity via URIs. These can be URLs, but can also emails or IOTA indices.
An important detail to note is the previousMessageId. This is an important field as it links the new DID Document to the old DID Document, creating a chain.
Without setting this value, the new DID Document won't get used during resolution of the DID!

@param {{network: string, node: string}} clientConfig
*/
async function manipulateIdentity() {
async function manipulateIdentity(clientConfig) {
//Creates a new identity (See "create_did" example)
let { key, doc, messageId } = await createIdentity();
let { key, doc, messageId } = await createIdentity(clientConfig);

//Add a new VerificationMethod with a new KeyPair
const newKey = new KeyPair(KeyType.Ed25519);
Expand All @@ -37,14 +39,14 @@ async function manipulateIdentity() {
*/
doc.previousMessageId = messageId;

//Sign the DID Document with the appropriate key
// Sign the DID Document with the appropriate key.
doc.sign(key);

//Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
const nextMessageId = await publish(doc.toJSON(), CLIENT_CONFIG);
// Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
const nextMessageId = await publish(doc.toJSON(), clientConfig);

//Log the results
console.log(`Identity Update: ${EXPLORER_URL}/${nextMessageId}`);
// Log the results.
logExplorerUrl("Identity Update:", clientConfig.network, messageId);
return {key, newKey, doc, nextMessageId};
}

Expand Down
22 changes: 12 additions & 10 deletions bindings/wasm/examples/merkle_key.js
Expand Up @@ -3,19 +3,21 @@

const { Digest, checkCredential, KeyType, publish, VerifiableCredential, VerificationMethod, KeyCollection } = require('../node/identity_wasm')
const { createIdentity } = require('./create_did');
const { EXPLORER_URL, CLIENT_CONFIG } = require('./config')
const { logExplorerUrl } = require('./explorer_util')

/*
This example shows how to sign/revoke verifiable credentials on scale.
Instead of revoking the entire verification method, a single key can be revoked from a MerkleKeyCollection.
This MerkleKeyCollection can be created as a collection of a power of 2 amount of keys.
Every key should be used once by the issuer for signing a verifiable credential.
When the verifiable credential must be revoked, the issuer revokes the index of the revoked key.

@param {{network: string, node: string}} clientConfig
*/
async function merkleKey() {
async function merkleKey(clientConfig) {
//Creates new identities (See "create_did" example)
const alice = await createIdentity();
const issuer = await createIdentity();
const alice = await createIdentity(clientConfig);
const issuer = await createIdentity(clientConfig);

//Add a Merkle Key Collection Verification Method with 8 keys (Must be a power of 2)
const keys = new KeyCollection(KeyType.Ed25519, 8);
Expand All @@ -27,8 +29,8 @@ async function merkleKey() {
issuer.doc.sign(issuer.key);

//Publish the Identity to the IOTA Network and log the results, this may take a few seconds to complete Proof-of-Work.
const nextMessageId = await publish(issuer.doc.toJSON(), CLIENT_CONFIG);
console.log(`Identity Update: ${EXPLORER_URL}/${nextMessageId}`);
const nextMessageId = await publish(issuer.doc.toJSON(), clientConfig);
logExplorerUrl("Identity Update:", clientConfig.network, nextMessageId);

// Prepare a credential subject indicating the degree earned by Alice
let credentialSubject = {
Expand Down Expand Up @@ -56,17 +58,17 @@ async function merkleKey() {
});

//Check the verifiable credential
const result = await checkCredential(signedVc.toString(), CLIENT_CONFIG);
const result = await checkCredential(signedVc.toString(), clientConfig);
console.log(`VC verification result: ${result.verified}`);

// The Issuer would like to revoke the credential (and therefore revokes key 0)
issuer.doc.revokeMerkleKey(method.id.toString(), 0);
issuer.doc.previousMessageId = nextMessageId;
const revokeMessageId = await publish(issuer.doc.toJSON(), CLIENT_CONFIG);
console.log(`Identity Update: ${EXPLORER_URL}/${revokeMessageId}`);
const revokeMessageId = await publish(issuer.doc.toJSON(), clientConfig);
logExplorerUrl("Identity Update:", clientConfig.network, revokeMessageId);

//Check the verifiable credential
const newResult = await checkCredential(signedVc.toString(), CLIENT_CONFIG);
const newResult = await checkCredential(signedVc.toString(), clientConfig);
console.log(`VC verification result: ${newResult.verified}`);
}

Expand Down
15 changes: 8 additions & 7 deletions bindings/wasm/examples/node.js
Expand Up @@ -8,6 +8,7 @@ const { createVC } = require('./create_vc');
const { createVP } = require('./create_vp');
const { revokeVC } = require('./revocation');
const { merkleKey } = require('./merkle_key');
const { CLIENT_CONFIG } = require('./config')

async function main() {
//Check if an example is mentioned
Expand All @@ -19,19 +20,19 @@ async function main() {
let argument = process.argv[2];
switch(argument) {
case 'create_did':
return await createIdentity();
return await createIdentity(CLIENT_CONFIG);
case 'manipulate_did':
return await manipulateIdentity();
return await manipulateIdentity(CLIENT_CONFIG);
case 'resolution':
return await resolution();
return await resolution(CLIENT_CONFIG);
case 'create_vc':
return await createVC();
return await createVC(CLIENT_CONFIG);
case 'revocation':
return await revokeVC();
return await revokeVC(CLIENT_CONFIG);
case 'create_vp':
return await createVP();
return await createVP(CLIENT_CONFIG);
case 'merkle_key':
return await merkleKey();
return await merkleKey(CLIENT_CONFIG);
default:
throw 'Unknown example name';
}
Expand Down
11 changes: 6 additions & 5 deletions bindings/wasm/examples/resolution.js
Expand Up @@ -3,17 +3,18 @@

const { resolve } = require('../node/identity_wasm')
const { manipulateIdentity } = require('./manipulate_did');
const { CLIENT_CONFIG } = require('./config');

/*
A short example to show how to resolve a DID. This returns the latest DID Document.

@param {{network: string, node: string}} clientConfig
*/
async function resolution() {
//Creates a new identity, that also is updated (See "manipulate_did" example)
async function resolution(clientConfig) {
// Creates a new identity, that also is updated (See "manipulate_did" example).
const result = await manipulateIdentity();

//Resolve a DID
return await resolve(result.doc.id.toString(), CLIENT_CONFIG);
// Resolve a DID.
return await resolve(result.doc.id.toString(), clientConfig);
}

exports.resolution = resolution;