From a7cfab4ee3229a0f5cf405af38abdaa73ff22251 Mon Sep 17 00:00:00 2001 From: Jelle Femmo Millenaar Date: Fri, 15 Oct 2021 09:05:48 +0200 Subject: [PATCH] Chore/combine examples (#420) * Merged Stronghold with basic examples in account * Updated explorer to identity resolver * Added explorer URL after basic example * Renamed examples * Completed manipulate did example * Fixed suggestions, removed replaced examples. * Improved example readme * Consistently use Stronghold and Resolver URL * Fix examples * Merged config example with private tangle * low-level-api private-network example runs * cargo fmt * cargo fmt with nightly * Impl suggestions --- .gitignore | 3 + examples/Cargo.toml | 20 ++---- examples/README.md | 17 +++-- examples/account/basic.rs | 40 ----------- examples/account/config.rs | 45 +++++++++++- examples/account/create_did.rs | 52 ++++++++++++++ examples/account/lazy.rs | 34 ++++++--- examples/account/manipulate_did.rs | 88 ++++++++++++++++++++++++ examples/account/methods.rs | 87 ----------------------- examples/account/private_tangle.rs | 75 -------------------- examples/account/services.rs | 45 ------------ examples/account/signing.rs | 43 +++++++++--- examples/account/stronghold.rs | 37 ---------- examples/low-level-api/private_tangle.rs | 24 +++++-- identity-iota/src/tangle/network.rs | 4 +- 15 files changed, 274 insertions(+), 340 deletions(-) delete mode 100644 examples/account/basic.rs create mode 100644 examples/account/create_did.rs create mode 100644 examples/account/manipulate_did.rs delete mode 100644 examples/account/methods.rs delete mode 100644 examples/account/private_tangle.rs delete mode 100644 examples/account/services.rs delete mode 100644 examples/account/stronghold.rs diff --git a/.gitignore b/.gitignore index cb628ce6eb..72a28a205e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,7 @@ book # ignore index.html index.html +# ignore files generated by example +*.hodl + !/bindings/wasm/static/index.html diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 8f38720d2e..852dc8c36e 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -15,37 +15,25 @@ name = "getting_started" path = "getting_started.rs" [[example]] -name = "account_basic" -path = "account/basic.rs" +name = "account_create" +path = "account/create_did.rs" [[example]] name = "account_config" path = "account/config.rs" [[example]] -name = "account_methods" -path = "account/methods.rs" +name = "account_manipulate" +path = "account/manipulate_did.rs" [[example]] name = "account_lazy" path = "account/lazy.rs" -[[example]] -name = "account_services" -path = "account/services.rs" - [[example]] name = "account_signing" path = "account/signing.rs" -[[example]] -name = "account_stronghold" -path = "account/stronghold.rs" - -[[example]] -name = "account_private_tangle" -path = "account/private_tangle.rs" - [[example]] name = "create_did" path = "low-level-api/create_did.rs" diff --git a/examples/README.md b/examples/README.md index 88162bfdfe..2fca72270f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,17 +18,16 @@ For Instance, to run the example `getting_started`, use cargo run --example getting_started ``` -The following examples are available for using the basic account (A high level API): +The following examples are available for using the basic account (A high-level API): | # | Name | Information | | :--: | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | | 1 | [getting_started](./getting_started.rs) | Introductory example for you to test whether the library is set up / working properly and compiles. | -| 2 | [account_basic](./account/basic.rs) | A basic example that generates and publishes a DID Document, the fundamental building block for decentralized identity. | -| 3 | [account_config](./account/config.rs) | This example goes into more detail regarding the usage of DID Documents. | -| 4 | [account_methods](./account/methods.rs) | A basic example that generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (vc) specifying claims about the subject, and retrieves information through the CredentialValidator API. | -| 5 | [account_services](./account/services.rs) | This example explains how to create a Verifiable Presentation from a set of credentials and sign it. | -| 6 | [account_signing](./account/signing.rs) | A basic example that generates a DID Document, publishes it to the Tangle, and retrieves information through DID Document resolution/dereferencing. | -| 7 | [account_stronghold](./account/stronghold.rs) | An example that utilizes a diff and integration chain to publish updates to a DID Document. | +| 2 | [account_create_did](./account/create_did.rs) | A basic example that generates and publishes a DID Document, the fundamental building block for decentralized identity. | +| 3 | [account_config](./account/config.rs) | How to configure the account to work with different networks and other settings. | +| 4 | [account_manipulate](./account/manipulate_did.rs) | How to manipulate a DID Document by adding/removing Verification Methods and Services. | +| 5 | [account_lazy](./account/lazy.rs) | How to take control over publishing DID updates manually, instead of the default automated behavior. | +| 6 | [account_signing](./account/signing.rs) | Using a DID to sign arbitrary statements and validating them. | The following examples are available for using the low-level APIs, which provides more flexibility at the cost of complexity: @@ -36,10 +35,10 @@ The following examples are available for using the low-level APIs, which provide | # | Name | Information | | :--: | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | | 1 | [create_did](./low-level-api/create_did.rs) | A basic example that generates and publishes a DID Document, the fundamental building block for decentralized identity. | -| 2 | [manipulate_did](low-level-api/manipulate_did.rs) | This example demonstrates how to perform a basic update to integration chain of a DID Document. | +| 2 | [manipulate_did](low-level-api/manipulate_did.rs) | This example demonstrates how to perform a basic update to the integration chain of a DID Document. | | 3 | [diff_chain](low-level-api/diff_chain.rs) | This example demonstrates how to perform a basic update to the diff chain of a DID Document. | | 4 | [resolve_history](low-level-api/resolve_history.rs) | Advanced example that performs multiple diff chain and integration chain updates and demonstrates how to resolve the DID Document history to view these chains. | -| 5 | [create_vc](./low-level-api/create_vc.rs) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (vc) specifying claims about the subject, and retrieves information through the CredentialValidator API. | +| 5 | [create_vc](./low-level-api/create_vc.rs) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (VC) specifying claims about the subject, and retrieves information through the CredentialValidator API. | | 6 | [create_vp](./low-level-api/create_vp.rs) | This example explains how to create a Verifiable Presentation from a set of credentials and sign it. | | 7 | [resolution](./low-level-api/resolution.rs) | A basic example that shows how to retrieve information through DID Document resolution/dereferencing. | | 8 | [revoke_vc](./low-level-api/revoke_vc.rs) | A basic example that shows how to retrieve information through DID Document resolution/dereferencing. | diff --git a/examples/account/basic.rs b/examples/account/basic.rs deleted file mode 100644 index 1aace30814..0000000000 --- a/examples/account/basic.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_basic - -use identity::account::Account; -use identity::account::IdentityCreate; -use identity::account::IdentityState; -use identity::account::Result; -use identity::iota::IotaDID; -use identity::iota::IotaDocument; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // Create a new Account with the default configuration - let account: Account = Account::builder().build().await?; - - // Create a new Identity with default settings - let identity: IdentityState = account.create_identity(IdentityCreate::default()).await?; - - // Retrieve the DID from the newly created Identity state. - let did: &IotaDID = identity.try_did()?; - - println!("[Example] Local Document = {:#?}", identity.to_document()?); - println!("[Example] Local Document List = {:#?}", account.list_identities().await); - - // Fetch the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - let resolved: IotaDocument = account.resolve_identity(did).await?; - - println!("[Example] Tangle Document = {:#?}", resolved); - - // Delete the identity and all associated keys - account.delete_identity(did).await?; - - Ok(()) -} diff --git a/examples/account/config.rs b/examples/account/config.rs index d58dcdc37c..c6549084dc 100644 --- a/examples/account/config.rs +++ b/examples/account/config.rs @@ -6,13 +6,26 @@ use identity::account::Account; use identity::account::AccountStorage; use identity::account::AutoSave; +use identity::account::IdentityCreate; +use identity::account::IdentityState; use identity::account::Result; +use identity::iota::IotaDID; use identity::iota::Network; #[tokio::main] async fn main() -> Result<()> { pretty_env_logger::init(); + // Set-up for private Tangle + // You can use https://github.com/iotaledger/one-click-tangle for a local setup. + // The `network_name` needs to match the id of the network or a part of it. + // As an example we are treating the devnet as a `private-tangle`, so we use `dev`. + // Replace this with `tangle` if you run this against a one-click private tangle. + let network_name = "dev"; + let network = Network::try_from_name(network_name)?; + // In a locally running one-click tangle, this would often be `http://127.0.0.1:14265/` + let private_node_url = "https://api.lb-0.h.chrysalis-devnet.iota.cafe"; + // Create a new Account with explicit configuration let account: Account = Account::builder() .autosave(AutoSave::Never) // never auto-save. rely on the drop save @@ -21,18 +34,46 @@ async fn main() -> Result<()> { .dropsave(false) // save the account state on drop .milestone(4) // save a snapshot every 4 actions .storage(AccountStorage::Memory) // use the default in-memory storage adapter - // configure the mainnet Tangle client + // configure a mainnet Tangle client with node and permanode .client(Network::Mainnet, |builder| { builder + // Manipulate this in order to manually appoint nodes .node("https://chrysalis-nodes.iota.org") .unwrap() // unwrap is safe, we provided a valid node URL + // Set a permanode from the same network (Important) .permanode("https://chrysalis-chronicle.iota.org/api/mainnet/", None, None) .unwrap() // unwrap is safe, we provided a valid permanode URL }) + // Configure a client for the private network, here `dev` + // Also set the URL that points to the REST API of the node + .client(network, |builder| { + // unwrap is safe, we provided a valid node URL + builder.node(private_node_url).unwrap() + }) .build() .await?; - println!("[Example] Account = {:#?}", account); + // Create an Identity specifically on the devnet by passing `network_name` + // The same applies if we wanted to create an identity on a private tangle + let id_create = IdentityCreate::new().network(network_name)?; + + // Create a new Identity with the network name set. + let identity: IdentityState = match account.create_identity(id_create).await { + Ok(identity) => identity, + Err(err) => { + eprintln!("[Example] Error: {:?} {}", err, err.to_string()); + eprintln!("[Example] Is your Tangle node listening on {}?", private_node_url); + return Ok(()); + } + }; + let iota_did: &IotaDID = identity.try_did()?; + + // Prints the Identity Resolver Explorer URL, the entire history can be observed on this page by "Loading History". + println!( + "[Example] Explore the DID Document = {}/{}", + iota_did.network()?.explorer_url().unwrap().to_string(), + iota_did.to_string() + ); Ok(()) } diff --git a/examples/account/create_did.rs b/examples/account/create_did.rs new file mode 100644 index 0000000000..bd0ef05985 --- /dev/null +++ b/examples/account/create_did.rs @@ -0,0 +1,52 @@ +// Copyright 2020-2021 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//! cargo run --example account_create + +use std::path::PathBuf; + +use identity::account::Account; +use identity::account::AccountStorage; +use identity::account::IdentityCreate; +use identity::account::IdentityState; +use identity::account::Result; +use identity::iota::IotaDID; + +#[tokio::main] +async fn main() -> Result<()> { + pretty_env_logger::init(); + + // Sets the location and password for the Stronghold + // + // Stronghold is an encrypted file that manages private keys. + // It implements best practices for security and is the recommended way of handling private keys. + let stronghold_path: PathBuf = "./example-strong.hodl".into(); + let password: String = "my-password".into(); + + // Create a new Account with the default configuration + let account: Account = Account::builder() + .storage(AccountStorage::Stronghold(stronghold_path, Some(password))) + .build() + .await?; + + // Create a new Identity with default settings + // + // This step generates a keypair, creates an identity and publishes it to the IOTA mainnet. + let identity: IdentityState = account.create_identity(IdentityCreate::default()).await?; + let iota_did: &IotaDID = identity.try_did()?; + + // Print the local state of the DID Document + println!( + "[Example] Local Document from {} = {:#?}", + iota_did, + identity.to_document() + ); + + // Prints the Identity Resolver Explorer URL, the entire history can be observed on this page by "Loading History". + println!( + "[Example] Explore the DID Document = {}/{}", + iota_did.network()?.explorer_url().unwrap().to_string(), + iota_did.to_string() + ); + Ok(()) +} diff --git a/examples/account/lazy.rs b/examples/account/lazy.rs index f0b28c4c61..433832e72c 100644 --- a/examples/account/lazy.rs +++ b/examples/account/lazy.rs @@ -2,8 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 //! cargo run --example account_lazy +use std::path::PathBuf; use identity::account::Account; +use identity::account::AccountStorage; use identity::account::IdentityCreate; use identity::account::IdentityState; use identity::account::Result; @@ -14,20 +16,28 @@ use identity::iota::IotaDID; async fn main() -> Result<()> { pretty_env_logger::init(); + // Stronghold settings + let stronghold_path: PathBuf = "./example-strong.hodl".into(); + let password: String = "my-password".into(); + // Create a new Account with auto publishing set to false. // This means updates are not pushed to the tangle automatically. // Rather, when we publish, multiple updates are batched together. - let account: Account = Account::builder().autopublish(false).build().await?; + let account: Account = Account::builder() + .storage(AccountStorage::Stronghold(stronghold_path, Some(password))) + .autopublish(false) + .build() + .await?; // Create a new Identity with default settings. // The identity will only be written to the local storage - not published to the tangle. let identity: IdentityState = account.create_identity(IdentityCreate::default()).await?; // Retrieve the DID from the newly created Identity state. - let did: &IotaDID = identity.try_did()?; + let iota_did: &IotaDID = identity.try_did()?; account - .update_identity(did) + .update_identity(iota_did) .create_service() .fragment("example-service") .type_("LinkedDomains") @@ -37,11 +47,11 @@ async fn main() -> Result<()> { // Publish the newly created DID document, // including the new service, to the tangle. - account.publish_updates(did).await?; + account.publish_updates(iota_did).await?; // Add another service. account - .update_identity(did) + .update_identity(iota_did) .create_service() .fragment("another-service") .type_("LinkedDomains") @@ -51,19 +61,21 @@ async fn main() -> Result<()> { // Delete the previously added service. account - .update_identity(did) + .update_identity(iota_did) .delete_service() .fragment("example-service") .apply() .await?; // Publish the updates as one message to the tangle. - account.publish_updates(did).await?; - - // Resolve the document to confirm its consistency. - let doc = account.resolve_identity(did).await?; + account.publish_updates(iota_did).await?; - println!("[Example] Document: {:#?}", doc); + // Prints the Identity Resolver Explorer URL, the entire history can be observed on this page by "Loading History". + println!( + "[Example] Explore the DID Document = {}/{}", + iota_did.network()?.explorer_url().unwrap().to_string(), + iota_did.to_string() + ); Ok(()) } diff --git a/examples/account/manipulate_did.rs b/examples/account/manipulate_did.rs new file mode 100644 index 0000000000..eeae96818f --- /dev/null +++ b/examples/account/manipulate_did.rs @@ -0,0 +1,88 @@ +// Copyright 2020-2021 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//! cargo run --example account_manipulate + +use std::path::PathBuf; + +use identity::account::Account; +use identity::account::AccountStorage; +use identity::account::IdentityCreate; +use identity::account::IdentityState; +use identity::account::Result; +use identity::core::Url; +use identity::did::MethodScope; +use identity::iota::IotaDID; + +#[tokio::main] +async fn main() -> Result<()> { + pretty_env_logger::init(); + + // =========================================================================== + // Create Identity - Similar to create_did example + // =========================================================================== + + // Stronghold settings + let stronghold_path: PathBuf = "./example-strong.hodl".into(); + let password: String = "my-password".into(); + + // Create a new Account with the default configuration + let account: Account = Account::builder() + .storage(AccountStorage::Stronghold(stronghold_path, Some(password))) + .build() + .await?; + + // Create a new Identity with default settings + // + // This step generates a keypair, creates an identity and publishes it to the IOTA mainnet. + let identity: IdentityState = account.create_identity(IdentityCreate::default()).await?; + let iota_did: &IotaDID = identity.try_did()?; + + // =========================================================================== + // Identity Manipulation + // =========================================================================== + + // Add another Ed25519 verification method to the identity + account + .update_identity(&iota_did) + .create_method() + .fragment("my-next-key") + .apply() + .await?; + + // Associate the newly created method with additional verification relationships + account + .update_identity(&iota_did) + .attach_method() + .fragment("my-next-key") + .scope(MethodScope::CapabilityDelegation) + .scope(MethodScope::CapabilityInvocation) + .apply() + .await?; + + // Add a new service to the identity. + account + .update_identity(&iota_did) + .create_service() + .fragment("my-service-1") + .type_("MyCustomService") + .endpoint(Url::parse("https://example.com")?) + .apply() + .await?; + + // Remove the Ed25519 verification method + account + .update_identity(&iota_did) + .delete_method() + .fragment("my-next-key") + .apply() + .await?; + + // Prints the Identity Resolver Explorer URL, the entire history can be observed on this page by "Loading History". + println!( + "[Example] Explore the DID Document = {}/{}", + iota_did.network()?.explorer_url().unwrap().to_string(), + iota_did.to_string() + ); + Ok(()) +} diff --git a/examples/account/methods.rs b/examples/account/methods.rs deleted file mode 100644 index d2f895a802..0000000000 --- a/examples/account/methods.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_methods - -use identity::account::Account; -use identity::account::IdentityCreate; -use identity::account::IdentityState; -use identity::account::Result; -use identity::did::MethodScope; -use identity::iota::IotaDID; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // Create a new Account with the default configuration - let account: Account = Account::builder().build().await?; - - // Create a new Identity with default settings - let identity: IdentityState = account.create_identity(IdentityCreate::default()).await?; - - // Retrieve the DID from the newly created Identity state. - let did: &IotaDID = identity.try_did()?; - - // Add a new Ed25519 (default) verification method to the identity - the - // verification method is included as an embedded authentication method. - account - .update_identity(did) - .create_method() - .scope(MethodScope::Authentication) - .fragment("my-auth-key") - .apply() - .await?; - - // Fetch and log the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - println!( - "[Example] Tangle Document (1) = {:#?}", - account.resolve_identity(did).await? - ); - - // Add another Ed25519 verification method to the identity - account - .update_identity(did) - .create_method() - .fragment("my-next-key") - .apply() - .await?; - - // Associate the newly created method with additional verification relationships - account - .update_identity(did) - .attach_method() - .fragment("my-next-key") - .scope(MethodScope::CapabilityDelegation) - .scope(MethodScope::CapabilityInvocation) - .apply() - .await?; - - // Fetch and log the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - println!( - "[Example] Tangle Document (2) = {:#?}", - account.resolve_identity(did).await? - ); - - // Remove the original Ed25519 verification method - account - .update_identity(did) - .delete_method() - .fragment("my-auth-key") - .apply() - .await?; - - // Fetch and log the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - println!( - "[Example] Tangle Document (3) = {:#?}", - account.resolve_identity(did).await? - ); - - Ok(()) -} diff --git a/examples/account/private_tangle.rs b/examples/account/private_tangle.rs deleted file mode 100644 index 788f34f109..0000000000 --- a/examples/account/private_tangle.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A basic example that generates, publishes and deletes a DID Document -//! to and from a private tangle. -//! It can be run together with a local hornet node. -//! Refer to https://github.com/iotaledger/one-click-tangle/tree/chrysalis/hornet-private-net -//! for setup instructions. -//! -//! cargo run --example account_private_tangle - -use identity::account::Account; -use identity::account::IdentityCreate; -use identity::account::IdentityState; -use identity::account::Result; -use identity::iota::IotaDID; -use identity::iota::IotaDocument; -use identity::iota::Network; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // This name needs to match the id of the network or part of it. - // Since the id of the one-click private tangle is `private-tangle` - // but we can only use 6 characters, we use just `tangle`. - let network_name = "tangle"; - let network = Network::try_from_name(network_name)?; - - // Create a new Account with a custom client configuration. - let private_node_url = "http://127.0.0.1:14265/"; - let account: Account = Account::builder() - // Configure a client for the private network. - // Also set the URL that points to the REST API - // of the locally running hornet node. - .client(network, |builder| { - // unwrap is safe, we provided a valid node URL - builder.node(private_node_url).unwrap() - }) - .build() - .await?; - - let id_create = IdentityCreate::new().network(network_name)?; - - // Create a new Identity with the network name set. - let identity: IdentityState = match account.create_identity(id_create).await { - Ok(identity) => identity, - Err(err) => { - eprintln!("[Example] Error: {:?} {}", err, err.to_string()); - eprintln!( - "[Example] Is your private Tangle node listening on {}?", - private_node_url - ); - return Ok(()); - } - }; - - // Retrieve the DID from the newly created Identity state. - let did: &IotaDID = identity.try_did()?; - - println!("[Example] Local Document = {:#?}", identity.to_document()?); - println!("[Example] Local Document List = {:#?}", account.list_identities().await); - - // Fetch the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - let resolved: IotaDocument = account.resolve_identity(did).await?; - - println!("[Example] Tangle Document = {:#?}", resolved); - - // Delete the identity and all associated keys - account.delete_identity(did).await?; - - Ok(()) -} diff --git a/examples/account/services.rs b/examples/account/services.rs deleted file mode 100644 index 636b54396a..0000000000 --- a/examples/account/services.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_services - -use identity::account::Account; -use identity::account::IdentityCreate; -use identity::account::IdentityState; -use identity::account::Result; -use identity::core::Url; -use identity::iota::IotaDID; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // Create a new Account with the default configuration - let account: Account = Account::builder().build().await?; - - // Create a new Identity with default settings - let identity: IdentityState = account.create_identity(IdentityCreate::default()).await?; - - // Retrieve the DID from the newly created Identity state. - let did: &IotaDID = identity.try_did()?; - - // Add a new service to the identity. - account - .update_identity(did) - .create_service() - .fragment("my-service-1") - .type_("MyCustomService") - .endpoint(Url::parse("https://example.com")?) - .apply() - .await?; - - // Fetch and log the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - println!( - "[Example] Tangle Document (1) = {:#?}", - account.resolve_identity(did).await? - ); - - Ok(()) -} diff --git a/examples/account/signing.rs b/examples/account/signing.rs index 7794956295..7b5f659cbc 100644 --- a/examples/account/signing.rs +++ b/examples/account/signing.rs @@ -3,7 +3,10 @@ //! cargo run --example account_signing +use std::path::PathBuf; + use identity::account::Account; +use identity::account::AccountStorage; use identity::account::IdentityCreate; use identity::account::IdentityState; use identity::account::Result; @@ -20,20 +23,33 @@ use identity::iota::IotaDocument; async fn main() -> Result<()> { pretty_env_logger::init(); + // =========================================================================== + // Create Identity - Similar to create_did example + // =========================================================================== + + // Stronghold settings + let stronghold_path: PathBuf = "./example-strong.hodl".into(); + let password: String = "my-password".into(); + // Create a new Account with the default configuration - let account: Account = Account::builder().build().await?; + let account: Account = Account::builder() + .storage(AccountStorage::Stronghold(stronghold_path, Some(password))) + .build() + .await?; // Create a new Identity with default settings + // + // This step generates a keypair, creates an identity and publishes it to the IOTA mainnet. let identity: IdentityState = account.create_identity(IdentityCreate::default()).await?; + let iota_did: &IotaDID = identity.try_did()?; - // Retrieve the DID from the newly created Identity state. - let did: &IotaDID = identity.try_did()?; - - println!("[Example] Local Document = {:#?}", identity.to_document()?); + // =========================================================================== + // Signing Example + // =========================================================================== // Add a new Ed25519 Verification Method to the identity account - .update_identity(did) + .update_identity(&iota_did) .create_method() .fragment("key-1") .apply() @@ -54,22 +70,27 @@ async fn main() -> Result<()> { // Issue an unsigned Credential... let mut credential: Credential = Credential::builder(Default::default()) - .issuer(Url::parse(did.as_str())?) + .issuer(Url::parse(&iota_did.as_str())?) .type_("UniversityDegreeCredential") .subject(subject) .build()?; // ...and sign the Credential with the previously created Verification Method - account.sign(did, "key-1", &mut credential).await?; + account.sign(&iota_did, "key-1", &mut credential).await?; println!("[Example] Local Credential = {:#}", credential); // Fetch the DID Document from the Tangle // // This is an optional step to ensure DID Document consistency. - let resolved: IotaDocument = account.resolve_identity(did).await?; - - println!("[Example] Tangle Document = {:#?}", resolved); + let resolved: IotaDocument = account.resolve_identity(&iota_did).await?; + + // Prints the Identity Resolver Explorer URL, the entire history can be observed on this page by "Loading History". + println!( + "[Example] Explore the DID Document = {}/{}", + iota_did.network()?.explorer_url().unwrap().to_string(), + iota_did.to_string() + ); // Ensure the resolved DID Document can verify the credential signature let verified: bool = resolved.verify_data(&credential).is_ok(); diff --git a/examples/account/stronghold.rs b/examples/account/stronghold.rs deleted file mode 100644 index 242071dea4..0000000000 --- a/examples/account/stronghold.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_stronghold - -use std::path::PathBuf; - -use identity::account::Account; -use identity::account::AccountStorage; -use identity::account::IdentityCreate; -use identity::account::IdentityState; -use identity::account::Result; -use identity::account::Stronghold; -use identity::iota::IotaDID; -use identity::iota::IotaDocument; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - let snapshot: PathBuf = "./example-strong.hodl".into(); - let password: Option<&str> = Some("my-password"); - - // Create a new Account with Stronghold as the storage adapter - // let account: Account = Account::builder() - // .storage(AccountStorage::Stronghold(snapshot, Some(password))) - // .build() - // .await?; - let adapter: Stronghold = Stronghold::new(&snapshot, password).await?; - - // Create a new Identity with default settings - let identity1: Account = Account::create_identity(Default::default(), adapter, IdentityCreate::default()).await?; - - println!("[Example] DID = {:#?}", identity1.did()); - - Ok(()) -} diff --git a/examples/low-level-api/private_tangle.rs b/examples/low-level-api/private_tangle.rs index b123abbd6f..f532eac7f8 100644 --- a/examples/low-level-api/private_tangle.rs +++ b/examples/low-level-api/private_tangle.rs @@ -10,8 +10,10 @@ //! cargo run --example private_tangle use identity::iota::ClientBuilder; +use identity::iota::IotaDID; use identity::iota::Network; use identity::iota::Receipt; +use identity::iota::TangleRef; use identity::prelude::*; #[tokio::main] @@ -19,11 +21,14 @@ pub async fn main() -> Result<()> { // This name needs to match the id of the network or part of it. // Since the id of the one-click private tangle is `private-tangle` // but we can only use 6 characters, we use just `tangle`. - let network = Network::try_from_name("tangle")?; + // As an example we are treating the devnet as a `private-tangle`, + // there are easier ways to change to devnet via `Network::Devnet` + let network = Network::try_from_name("dev")?; // Set the network and the URL that points to - // the REST API of the locally running hornet node. - let private_node_url = "http://127.0.0.1:14265/"; + // the REST API of the node. + // In a locally running private tangle, this would often be `http://127.0.0.1:14265/` + let private_node_url = "https://api.lb-0.h.chrysalis-devnet.iota.cafe"; let client = ClientBuilder::new() .network(network) .node(private_node_url)? @@ -40,8 +45,6 @@ pub async fn main() -> Result<()> { // Sign the DID Document with the default authentication key. document.sign(keypair.private())?; - println!("DID Document JSON > {:#}", document); - // Publish the DID Document to the Tangle. let receipt: Receipt = match client.publish_document(&document).await { Ok(receipt) => receipt, @@ -54,5 +57,16 @@ pub async fn main() -> Result<()> { println!("Publish Receipt > {:#?}", receipt); + // Prints the Identity Resolver Explorer URL, the entire history can be observed on this page by "Loading History". + let iota_did: &IotaDID = document.did(); + println!( + "[Example] Explore the DID Document = {}", + format!( + "{}/{}", + iota_did.network()?.explorer_url().unwrap().to_string(), + iota_did.to_string() + ) + ); + Ok(()) } diff --git a/identity-iota/src/tangle/network.rs b/identity-iota/src/tangle/network.rs index 396695fc11..45ca9f808f 100644 --- a/identity-iota/src/tangle/network.rs +++ b/identity-iota/src/tangle/network.rs @@ -15,8 +15,8 @@ const NETWORK_NAME_MAIN: &str = "main"; const NETWORK_NAME_DEV: &str = "dev"; lazy_static! { - static ref EXPLORER_MAIN: Url = Url::parse("https://explorer.iota.org/mainnet").unwrap(); - static ref EXPLORER_DEV: Url = Url::parse("https://explorer.iota.org/devnet").unwrap(); + static ref EXPLORER_MAIN: Url = Url::parse("https://explorer.iota.org/mainnet/identity-resolver").unwrap(); + static ref EXPLORER_DEV: Url = Url::parse("https://explorer.iota.org/devnet/identity-resolver/").unwrap(); static ref NODE_MAIN: Url = Url::parse("https://chrysalis-nodes.iota.org").unwrap(); static ref NODE_DEV: Url = Url::parse("https://api.lb-0.h.chrysalis-devnet.iota.cafe").unwrap(); }