From 843a1124b85c76e3a34f6f5839887439fab2a542 Mon Sep 17 00:00:00 2001 From: Matt Renaud Date: Thu, 20 May 2021 19:32:00 -0700 Subject: [PATCH 1/6] Add IotaDocument::from_keypair_with_network() and WASM binding. --- bindings/wasm/README.md | 65 +++++++++++-------- bindings/wasm/docs/api-reference.md | 13 ++++ bindings/wasm/src/wasm_document.rs | 8 +++ identity-iota/src/did/doc/iota_document.rs | 39 +++++++++++ .../src/did/doc/iota_verification_method.rs | 11 ++++ 5 files changed, 110 insertions(+), 26 deletions(-) diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index eb4cf31edc..d58eb01921 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -7,6 +7,7 @@ ## Install the library: Latest Release: This version matches the main branch of this repository, is stable and will have changelogs. + ```bash $ npm install @iota/identity-wasm // or using yarn @@ -14,6 +15,7 @@ $ yarn add @iota/identity-wasm ``` Development Release: This version matches the dev branch of this repository, may see frequent breaking changes and has the latest code changes. + ```bash $ npm install @iota/identity-wasm@dev // or using yarn @@ -23,25 +25,30 @@ $ yarn add @iota/identity-wasm@dev ## NodeJS Setup ```js -const identity = require('@iota/identity-wasm/node') +const identity = require("@iota/identity-wasm/node"); // Generate a new KeyPair -const key = new identity.KeyPair(identity.KeyType.Ed25519) +const key = new identity.KeyPair(identity.KeyType.Ed25519); // Create a new DID Document with the KeyPair as the default authentication method -const doc = identity.Document.fromKeyPair(key) +const doc = identity.Document.fromKeyPair(key); // Sign the DID Document with the sceret key -doc.sign(key) +doc.sign(key); // Publish the DID Document to the IOTA Tangle -identity.publish(doc.toJSON(), { node: "https://nodes.thetangle.org:443" }) +identity + .publish(doc.toJSON(), { node: "https://nodes.thetangle.org:443" }) .then((message) => { - console.log("Tangle Message Id: ", message) - console.log("Tangle Message Url", `https://explorer.iota.org/mainnet/transaction/${message}`) - }).catch((error) => { - console.error("Error: ", error) + console.log("Tangle Message Id: ", message); + console.log( + "Tangle Message Url", + `https://explorer.iota.org/mainnet/transaction/${message}`, + ); }) + .catch((error) => { + console.error("Error: ", error); + }); ``` ## Web Setup @@ -62,16 +69,18 @@ $ yarn add rollup-plugin-copy --dev ```js // Include the copy plugin -import copy from 'rollup-plugin-copy' +import copy from "rollup-plugin-copy"; // Add the copy plugin to the `plugins` array of your rollup config: copy({ - targets: [{ - src: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', - dest: 'public', - rename: 'identity_wasm_bg.wasm' - }] -}) + targets: [ + { + src: "node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm", + dest: "public", + rename: "identity_wasm_bg.wasm", + }, + ], +}); ``` ### Webpack @@ -106,21 +115,25 @@ new CopyWebPlugin({ import * as identity from "@iota/identity-wasm/web"; identity.init().then(() => { - const key = new identity.KeyPair(identity.KeyType.Ed25519) - const doc = identity.Document.fromKeyPair(key) - console.log("Key Pair", key) - console.log("DID Document: ", doc) + const key = new identity.KeyPair(identity.KeyType.Ed25519); + const doc = identity.Document.fromKeyPair(key); + // Or, if using the testnet: + // const doc = identity.Document.fromKeyPairWithNetwork(key, "test") + console.log("Key Pair", key); + console.log("DID Document: ", doc); }); // or (async () => { - await identity.init() - const key = new identity.KeyPair(identity.KeyType.Ed25519) - const doc = identity.Document.fromKeyPair(key) - console.log("Key Pair", key) - console.log("DID Document: ", doc) -})() + await identity.init(); + const key = new identity.KeyPair(identity.KeyType.Ed25519); + const doc = identity.Document.fromKeyPair(key); + // Or, if using the testnet: + // const doc = identity.Document.fromKeyPairWithNetwork(key, "test") + console.log("Key Pair", key); + console.log("DID Document: ", doc); +})(); // Default path is "identity_wasm_bg.wasm", but you can override it like this await identity.init("./static/identity_wasm_bg.wasm"); diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 6dffdcb246..272235bd93 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -532,6 +532,7 @@ Parses a `DID` from the input string. * [.toJSON()](#Document+toJSON) ⇒ any * _static_ * [.fromKeyPair(key)](#Document.fromKeyPair) ⇒ [Document](#Document) + * [.fromKeyPairWithNetwork(key, network)](#Document.fromKeyPairWithNetwork) ⇒ [Document](#Document) * [.fromAuthentication(method)](#Document.fromAuthentication) ⇒ [Document](#Document) * [.fromJSON(json)](#Document.fromJSON) ⇒ [Document](#Document) @@ -733,6 +734,18 @@ Creates a new DID Document from the given KeyPair. | --- | --- | | key | [KeyPair](#KeyPair) | + + +### Document.fromKeyPairWithNetwork(key, network) ⇒ [Document](#Document) +Creates a new DID Document from the given KeyPair on the specified network. + +**Kind**: static method of [Document](#Document) + +| Param | Type | +| --- | --- | +| key | [KeyPair](#KeyPair) | +| network | string | + ### Document.fromAuthentication(method) ⇒ [Document](#Document) diff --git a/bindings/wasm/src/wasm_document.rs b/bindings/wasm/src/wasm_document.rs index aa68c34ba3..e50bca0be7 100644 --- a/bindings/wasm/src/wasm_document.rs +++ b/bindings/wasm/src/wasm_document.rs @@ -86,6 +86,14 @@ impl WasmDocument { IotaDocument::from_keypair(&key.0).map_err(err).map(Self) } + /// Creates a new DID Document from the given KeyPair on the specified network. + #[wasm_bindgen(js_name = fromKeyPairWithNetwork)] + pub fn from_keypair_with_network(key: &KeyPair, network: &str) -> Result { + IotaDocument::from_keypair_with_network(&key.0, &network) + .map_err(err) + .map(Self) + } + /// Creates a new DID Document from the given verification [`method`][`Method`]. #[wasm_bindgen(js_name = fromAuthentication)] pub fn from_authentication(method: &WasmVerificationMethod) -> Result { diff --git a/identity-iota/src/did/doc/iota_document.rs b/identity-iota/src/did/doc/iota_document.rs index 427242d222..b4c5988b0d 100644 --- a/identity-iota/src/did/doc/iota_document.rs +++ b/identity-iota/src/did/doc/iota_document.rs @@ -82,6 +82,22 @@ impl IotaDocument { Ok(unsafe { Self::from_authentication_unchecked(method) }) } + /// Creates a new DID Document from the given KeyPair and network. + /// + /// The DID Document will be pre-populated with a single authentication + /// method based on the provided [KeyPair]. + /// + /// The authentication method will have the DID URL fragment `#authentication` + /// and can be easily retrieved with [Document::authentication]. + pub fn from_keypair_with_network(keypair: &KeyPair, network: &str) -> Result { + let method: IotaVerificationMethod = + IotaVerificationMethod::from_keypair_with_network(keypair, "authentication", network)?; + + // SAFETY: We don't create invalid Methods. Method::from_keypair() uses the MethodBuilder + // internally which verifies correctness on construction. + Ok(unsafe { Self::from_authentication_unchecked(method) }) + } + /// Creates a new DID Document from the given verification [`method`][VerificationMethod]. pub fn from_authentication(method: IotaVerificationMethod) -> Result { Self::check_authentication(&method)?; @@ -631,6 +647,8 @@ mod tests { const DID_ID: &str = "did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M"; const DID_AUTH: &str = "did:iota:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M#authentication"; + const DID_TESTNET_ID: &str = "did:iota:test:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M"; + const DID_TESTNET_AUTH: &str = "did:iota:test:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M#authentication"; fn valid_did() -> DID { DID_ID.parse().unwrap() @@ -716,6 +734,19 @@ mod tests { ); } + fn compare_document_testnet(document: &IotaDocument) { + assert_eq!(document.id().to_string(), DID_TESTNET_ID); + assert_eq!(document.authentication_id(), DID_TESTNET_AUTH); + assert_eq!( + document.authentication().key_type(), + MethodType::Ed25519VerificationKey2018 + ); + assert_eq!( + document.authentication().key_data(), + &MethodData::PublicKeyBase58(String::from("FJsXMk9UqpJf3ZTKnfEQAhvBrVLKMSx9ZeYwQME6c6tT")) + ); + } + #[test] fn test_invalid_try_from_core_invalid_id() { let invalid_did: DID = "did:invalid:HGE4tecHWL2YiZv5qAGtH7gaeQcaz2Z1CR15GWmMjY1M" @@ -891,6 +922,14 @@ mod tests { compare_document(&document); } + #[test] + fn test_from_keypair_with_network() { + //from keypair + let keypair: KeyPair = generate_testkey(); + let document: IotaDocument = IotaDocument::from_keypair_with_network(&keypair, "test").unwrap(); + compare_document_testnet(&document); + } + #[test] fn test_no_controler() { let keypair: KeyPair = generate_testkey(); diff --git a/identity-iota/src/did/doc/iota_verification_method.rs b/identity-iota/src/did/doc/iota_verification_method.rs index 08ed092bc4..7df5340aeb 100644 --- a/identity-iota/src/did/doc/iota_verification_method.rs +++ b/identity-iota/src/did/doc/iota_verification_method.rs @@ -66,6 +66,17 @@ impl IotaVerificationMethod { Self::from_did(did, keypair, fragment) } + /// Creates a new [`IotaVerificationMethod`] object from the given `keypair` on the specified `network`. + pub fn from_keypair_with_network<'a, F>(keypair: &KeyPair, fragment: F, network: &str) -> Result + where + F: Into>, + { + let key: &[u8] = keypair.public().as_ref(); + let did: IotaDID = IotaDID::with_network(key, &network)?; + + Self::from_did(did, keypair, fragment) + } + /// Creates a new [`Method`] object from the given `did` and `keypair`. /// /// If the `fragment` resolves to `Option::None` then the default verification method tag will be From 7f0379f08e1b3cea1b30b962bf9d90ca41fe1a62 Mon Sep 17 00:00:00 2001 From: Matt Renaud Date: Thu, 20 May 2021 19:38:33 -0700 Subject: [PATCH 2/6] Undo unintentional formatting in README. --- bindings/wasm/README.md | 63 ++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index d58eb01921..84f7781f17 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -7,7 +7,6 @@ ## Install the library: Latest Release: This version matches the main branch of this repository, is stable and will have changelogs. - ```bash $ npm install @iota/identity-wasm // or using yarn @@ -15,7 +14,6 @@ $ yarn add @iota/identity-wasm ``` Development Release: This version matches the dev branch of this repository, may see frequent breaking changes and has the latest code changes. - ```bash $ npm install @iota/identity-wasm@dev // or using yarn @@ -25,30 +23,25 @@ $ yarn add @iota/identity-wasm@dev ## NodeJS Setup ```js -const identity = require("@iota/identity-wasm/node"); +const identity = require('@iota/identity-wasm/node') // Generate a new KeyPair -const key = new identity.KeyPair(identity.KeyType.Ed25519); +const key = new identity.KeyPair(identity.KeyType.Ed25519) // Create a new DID Document with the KeyPair as the default authentication method -const doc = identity.Document.fromKeyPair(key); +const doc = identity.Document.fromKeyPair(key) // Sign the DID Document with the sceret key -doc.sign(key); +doc.sign(key) // Publish the DID Document to the IOTA Tangle -identity - .publish(doc.toJSON(), { node: "https://nodes.thetangle.org:443" }) +identity.publish(doc.toJSON(), { node: "https://nodes.thetangle.org:443" }) .then((message) => { - console.log("Tangle Message Id: ", message); - console.log( - "Tangle Message Url", - `https://explorer.iota.org/mainnet/transaction/${message}`, - ); + console.log("Tangle Message Id: ", message) + console.log("Tangle Message Url", `https://explorer.iota.org/mainnet/transaction/${message}`) + }).catch((error) => { + console.error("Error: ", error) }) - .catch((error) => { - console.error("Error: ", error); - }); ``` ## Web Setup @@ -69,18 +62,16 @@ $ yarn add rollup-plugin-copy --dev ```js // Include the copy plugin -import copy from "rollup-plugin-copy"; +import copy from 'rollup-plugin-copy' // Add the copy plugin to the `plugins` array of your rollup config: copy({ - targets: [ - { - src: "node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm", - dest: "public", - rename: "identity_wasm_bg.wasm", - }, - ], -}); + targets: [{ + src: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', + dest: 'public', + rename: 'identity_wasm_bg.wasm' + }] +}) ``` ### Webpack @@ -115,25 +106,25 @@ new CopyWebPlugin({ import * as identity from "@iota/identity-wasm/web"; identity.init().then(() => { - const key = new identity.KeyPair(identity.KeyType.Ed25519); - const doc = identity.Document.fromKeyPair(key); + const key = new identity.KeyPair(identity.KeyType.Ed25519) + const doc = identity.Document.fromKeyPair(key) // Or, if using the testnet: - // const doc = identity.Document.fromKeyPairWithNetwork(key, "test") - console.log("Key Pair", key); - console.log("DID Document: ", doc); + // const doc = identity.Document.fromKeyPairWithNetwork(key, "test") + console.log("Key Pair", key) + console.log("DID Document: ", doc) }); // or (async () => { - await identity.init(); - const key = new identity.KeyPair(identity.KeyType.Ed25519); - const doc = identity.Document.fromKeyPair(key); + await identity.init() + const key = new identity.KeyPair(identity.KeyType.Ed25519) + const doc = identity.Document.fromKeyPair(key) // Or, if using the testnet: // const doc = identity.Document.fromKeyPairWithNetwork(key, "test") - console.log("Key Pair", key); - console.log("DID Document: ", doc); -})(); + console.log("Key Pair", key) + console.log("DID Document: ", doc) +})() // Default path is "identity_wasm_bg.wasm", but you can override it like this await identity.init("./static/identity_wasm_bg.wasm"); From 4741ae19fd32d57c5855536b7b81639e5a94127e Mon Sep 17 00:00:00 2001 From: Matt Renaud Date: Tue, 25 May 2021 13:37:43 -0700 Subject: [PATCH 3/6] Add optional network arg to from_keypair WASM binding. --- bindings/wasm/src/wasm_document.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/bindings/wasm/src/wasm_document.rs b/bindings/wasm/src/wasm_document.rs index e50bca0be7..d71dac7705 100644 --- a/bindings/wasm/src/wasm_document.rs +++ b/bindings/wasm/src/wasm_document.rs @@ -80,18 +80,16 @@ impl WasmDocument { }) } - /// Creates a new DID Document from the given KeyPair. + /// Creates a new DID Document from the given KeyPair and optional network. + /// + /// If unspecified, network defaults to the mainnet. #[wasm_bindgen(js_name = fromKeyPair)] - pub fn from_keypair(key: &KeyPair) -> Result { - IotaDocument::from_keypair(&key.0).map_err(err).map(Self) - } - - /// Creates a new DID Document from the given KeyPair on the specified network. - #[wasm_bindgen(js_name = fromKeyPairWithNetwork)] - pub fn from_keypair_with_network(key: &KeyPair, network: &str) -> Result { - IotaDocument::from_keypair_with_network(&key.0, &network) - .map_err(err) - .map(Self) + pub fn from_keypair(key: &KeyPair, network: Option) -> Result { + let doc = match network { + Some(net) => IotaDocument::from_keypair_with_network(&key.0, &net), + None => IotaDocument::from_keypair(&key.0) + } + doc.map_err(err).map(Self) } /// Creates a new DID Document from the given verification [`method`][`Method`]. From fa12f9637c3976565e4f8578a2a92075a146333f Mon Sep 17 00:00:00 2001 From: Matt Renaud Date: Tue, 25 May 2021 13:39:50 -0700 Subject: [PATCH 4/6] Update API reference for fromKeyPair. --- bindings/wasm/docs/api-reference.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 272235bd93..db76e3c6d1 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -532,7 +532,6 @@ Parses a `DID` from the input string. * [.toJSON()](#Document+toJSON) ⇒ any * _static_ * [.fromKeyPair(key)](#Document.fromKeyPair) ⇒ [Document](#Document) - * [.fromKeyPairWithNetwork(key, network)](#Document.fromKeyPairWithNetwork) ⇒ [Document](#Document) * [.fromAuthentication(method)](#Document.fromAuthentication) ⇒ [Document](#Document) * [.fromJSON(json)](#Document.fromJSON) ⇒ [Document](#Document) @@ -726,25 +725,16 @@ Serializes a `Document` object as a JSON object. ### Document.fromKeyPair(key) ⇒ [Document](#Document) -Creates a new DID Document from the given KeyPair. +Creates a new DID Document from the given KeyPair and optional network. -**Kind**: static method of [Document](#Document) - -| Param | Type | -| --- | --- | -| key | [KeyPair](#KeyPair) | - - - -### Document.fromKeyPairWithNetwork(key, network) ⇒ [Document](#Document) -Creates a new DID Document from the given KeyPair on the specified network. +If unspecified, the network defaults to the mainnet. **Kind**: static method of [Document](#Document) | Param | Type | | --- | --- | | key | [KeyPair](#KeyPair) | -| network | string | +| network | string \| undefined | From a1dcd83da9556a2092ca9fd7496c769fc55c9812 Mon Sep 17 00:00:00 2001 From: Matt Renaud Date: Tue, 25 May 2021 13:40:35 -0700 Subject: [PATCH 5/6] Update README. --- bindings/wasm/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 84f7781f17..c3f7cf838f 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -109,7 +109,7 @@ identity.init().then(() => { const key = new identity.KeyPair(identity.KeyType.Ed25519) const doc = identity.Document.fromKeyPair(key) // Or, if using the testnet: - // const doc = identity.Document.fromKeyPairWithNetwork(key, "test") + // const doc = identity.Document.fromKeyPair(key, "test") console.log("Key Pair", key) console.log("DID Document: ", doc) }); @@ -121,7 +121,7 @@ identity.init().then(() => { const key = new identity.KeyPair(identity.KeyType.Ed25519) const doc = identity.Document.fromKeyPair(key) // Or, if using the testnet: - // const doc = identity.Document.fromKeyPairWithNetwork(key, "test") + // const doc = identity.Document.fromKeyPair(key, "test") console.log("Key Pair", key) console.log("DID Document: ", doc) })() From 9a40d53ad6b0fb578dac519adc891733d4a29687 Mon Sep 17 00:00:00 2001 From: Matt Renaud Date: Sat, 29 May 2021 09:59:04 -0700 Subject: [PATCH 6/6] Update bindings/wasm/src/wasm_document.rs Co-authored-by: Devin Turner <5410284+l1h3r@users.noreply.github.com> --- bindings/wasm/src/wasm_document.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/wasm/src/wasm_document.rs b/bindings/wasm/src/wasm_document.rs index d71dac7705..0902706904 100644 --- a/bindings/wasm/src/wasm_document.rs +++ b/bindings/wasm/src/wasm_document.rs @@ -88,7 +88,7 @@ impl WasmDocument { let doc = match network { Some(net) => IotaDocument::from_keypair_with_network(&key.0, &net), None => IotaDocument::from_keypair(&key.0) - } + }; doc.map_err(err).map(Self) }