diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index a9ccc1352a..e884a280ae 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -114,67 +114,18 @@ jobs:
# run examples only on ubuntu for now
if: matrix.os == 'ubuntu-latest'
run: |
- cargo read-manifest --manifest-path ./examples/Cargo.toml | \
+ cargo read-manifest --manifest-path ./examples_legacy/Cargo.toml | \
jq -r '.targets[].name' | \
parallel -k -j 4 --retries 3 cargo run --example {} --all-features --release
- - name: Stop sccache
- uses: './.github/actions/rust/sccache/stop-sccache'
- with:
- os: ${{matrix.os}}
-
- build-and-test-stardust:
- needs: [ check-for-run-condition, check-for-modification ]
- if: ${{ needs.check-for-run-condition.outputs.should-run == 'true' && needs.check-for-modification.outputs.core-modified == 'true' }}
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- os: [ ubuntu-latest ]
- include:
- - os: ubuntu-latest
- sccache-path: /home/runner/.cache/sccache
- env:
- SCCACHE_DIR: ${{ matrix.sccache-path }}
- RUSTC_WRAPPER: sccache
-
- steps:
- - uses: actions/checkout@v2
-
- - name: Setup Rust and cache
- uses: './.github/actions/rust/rust-setup'
- with:
- os: ${{ runner.os }}
- job: ${{ github.job }}
- target-cache-enabled: false
- sccache-enabled: true
- sccache-path: ${{ matrix.sccache-path }}
-
- - name: Setup sccache
- uses: './.github/actions/rust/sccache/setup-sccache'
- with:
- os: ${{matrix.os}}
-
- - name: Build Stardust
- uses: actions-rs/cargo@v1
- with:
- command: build
- args: --manifest-path ./identity_stardust/Cargo.toml --workspace --tests --examples --all-features --release
-
- - name: Run Stardust tests
- uses: actions-rs/cargo@v1
- with:
- command: test
- args: --manifest-path ./identity_stardust/Cargo.toml --workspace --all-features --release
-
- name: Run Stardust Rust examples
# run examples only on ubuntu for now
if: matrix.os == 'ubuntu-latest'
run: |
- cargo read-manifest --manifest-path ./identity_stardust/Cargo.toml | \
+ cargo read-manifest --manifest-path ./examples/Cargo.toml | \
jq -r '.targets[].name' | \
- awk '$1 ~ /ex.*/' | \
- parallel -k -j 4 --retries 3 cargo run --manifest-path ./identity_stardust/Cargo.toml --example {} --all-features --release
+ awk '$1 ~ /[0-9].*/' | \
+ parallel -k -j 4 --retries 3 cargo run --manifest-path ./examples/Cargo.toml --example {} --all-features --release
- name: Stop sccache
uses: './.github/actions/rust/sccache/stop-sccache'
diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml
index ce08221e1a..3005724418 100644
--- a/.github/workflows/clippy.yml
+++ b/.github/workflows/clippy.yml
@@ -43,13 +43,6 @@ jobs:
args: --all-targets --all-features -- -D warnings
name: core
- - name: Stardust clippy check
- uses: actions-rs/clippy-check@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- args: --manifest-path ./identity_stardust/Cargo.toml --all-targets --all-features -- -D warnings
- name: stardust
-
- name: Wasm clippy check
uses: actions-rs/clippy-check@v1
with:
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
index ae910b33a3..9e8894b595 100644
--- a/.github/workflows/format.yml
+++ b/.github/workflows/format.yml
@@ -41,12 +41,6 @@ jobs:
command: fmt
args: --all -- --check
- - name: Stardust fmt check
- uses: actions-rs/cargo@v1
- with:
- command: fmt
- args: --manifest-path ./identity_stardust/Cargo.toml --all -- --check
-
- name: wasm fmt check
uses: actions-rs/cargo@v1
with:
diff --git a/.meta/identity_banner.png b/.meta/identity_banner.png
deleted file mode 100644
index c78a328626..0000000000
Binary files a/.meta/identity_banner.png and /dev/null differ
diff --git a/Cargo.toml b/Cargo.toml
index e564cfa7e4..b4342ed792 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,9 @@ members = [
"identity_iota",
"identity_iota_client",
"identity_iota_core",
+ "identity_stardust",
+ "examples_legacy",
"examples",
]
diff --git a/README.md b/README.md
index d8f7db8ce6..135b9856b6 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-![banner](https://github.com/iotaledger/identity.rs/raw/HEAD/.meta/identity_banner.png)
+![banner](https://github.com/iotaledger/identity.rs/raw/HEAD/documentation/static/img/Banner/banner_identity.svg)
diff --git a/bindings/wasm/examples-account/README.md b/bindings/wasm/examples-account/README.md
index 64f137e3b0..d8f4c4e416 100644
--- a/bindings/wasm/examples-account/README.md
+++ b/bindings/wasm/examples-account/README.md
@@ -1,4 +1,4 @@
-![banner](./../../../.meta/identity_banner.png)
+![banner](./../../../documentation/static/img/Banner/banner_identity.svg)
## IOTA Identity Account Examples
diff --git a/bindings/wasm/examples-stardust/README.md b/bindings/wasm/examples-stardust/README.md
index ecce863248..6594c6597e 100644
--- a/bindings/wasm/examples-stardust/README.md
+++ b/bindings/wasm/examples-stardust/README.md
@@ -1,4 +1,4 @@
-![banner](./../../../.meta/identity_banner.png)
+![banner](./../../../documentation/static/img/Banner/banner_identity.svg)
## IOTA Identity UTXO Examples
diff --git a/bindings/wasm/examples-stardust/src/ex0_create_did.ts b/bindings/wasm/examples-stardust/src/ex0_create_did.ts
index ca690a9f05..e1fe698b31 100644
--- a/bindings/wasm/examples-stardust/src/ex0_create_did.ts
+++ b/bindings/wasm/examples-stardust/src/ex0_create_did.ts
@@ -15,9 +15,8 @@ import {Bip39} from "@iota/crypto.js";
import fetch from "node-fetch";
import {Client, MnemonicSecretManager, SecretManager} from "@cycraig/iota-client-wasm/node";
-const EXPLORER = "https://explorer.alphanet.iotaledger.net/alphanet";
-const API_ENDPOINT = "https://api.alphanet.iotaledger.net/";
-const FAUCET = "https://faucet.alphanet.iotaledger.net/api/enqueue";
+const API_ENDPOINT = "https://api.testnet.shimmer.network/";
+const FAUCET = "https://faucet.testnet.shimmer.network/api/enqueue";
/** Demonstrate how to create a DID Document and publish it in a new Alias Output. */
export async function createIdentity(): Promise<{
diff --git a/bindings/wasm/examples/README.md b/bindings/wasm/examples/README.md
index bb500f3526..09d6771208 100644
--- a/bindings/wasm/examples/README.md
+++ b/bindings/wasm/examples/README.md
@@ -1,4 +1,4 @@
-![banner](./../../../.meta/identity_banner.png)
+![banner](./../../../documentation/static/img/Banner/banner_identity.svg)
## IOTA Identity Examples
diff --git a/documentation/docs/concepts/decentralized_identifiers/create.mdx b/documentation/docs/concepts/decentralized_identifiers/create.mdx
index 1039d269aa..a9cd6d66bd 100644
--- a/documentation/docs/concepts/decentralized_identifiers/create.mdx
+++ b/documentation/docs/concepts/decentralized_identifiers/create.mdx
@@ -11,7 +11,7 @@ keywords:
- Publish
---
import CodeSnippet from '../../../src/components/CodeSnippetComponent'
-import createDidRustExample from '!!raw-loader!../../../../examples/account/create_did.rs';
+import createDidRustExample from '!!raw-loader!../../../../examples_legacy/account/create_did.rs';
When someone or something wants to benefit from Self-Sovereign Identity, they must first create a Decentralized Identity. This identity consists of many parts that have different functions. This page will cover both the basics and the details about identity creation, storage, and publishing to the Tangle.
@@ -29,7 +29,7 @@ Select your programming language of choice and press the green play button to ex
nodeReplitLink="https://repl.it/@IOTAFoundation/create-did?lite=true"
rustContent={createDidRustExample}
nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_did.ts"
- rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_did.rs"
+ rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/create_did.rs"
/>
The first step in this example is the creation of an account. This acts as a stateful object that manages one or more identities. The account provides an interface to execute high-level operations on identities, such as creating, updating, and storing them.
diff --git a/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx b/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx
index 6b02aa5832..8a26f834d8 100644
--- a/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx
+++ b/documentation/docs/concepts/decentralized_identifiers/private_tangle.mdx
@@ -11,8 +11,8 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
import private_tangle_js from '!!raw-loader!../../../../bindings/wasm/examples/src/private_tangle.js';
-import private_tangle_rs from '!!raw-loader!../../../../examples/low-level-api/private_tangle.rs';
-import account_private_tangle_rs from '!!raw-loader!../../../../examples/account/config.rs';
+import private_tangle_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/private_tangle.rs';
+import account_private_tangle_rs from '!!raw-loader!../../../../examples_legacy/account/config.rs';
## Example
diff --git a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx
index b636bbb5fb..78405873b6 100644
--- a/documentation/docs/concepts/decentralized_identifiers/resolve.mdx
+++ b/documentation/docs/concepts/decentralized_identifiers/resolve.mdx
@@ -6,8 +6,8 @@ image: /img/Identity_icon.png
keywords:
- Resolve
---
-import resolve_did_rs from '!!raw-loader!../../../../examples/low-level-api/resolve_did.rs';
-import resolve_history_rs from '!!raw-loader!../../../../examples/low-level-api/resolve_history.rs';
+import resolve_did_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/resolve_did.rs';
+import resolve_history_rs from '!!raw-loader!../../../../examples_legacy/low-level-api/resolve_history.rs';
import resolve_did_js from '!!raw-loader!../../../../bindings/wasm/examples/src/resolve_did.js';
import resolve_history_js from '!!raw-loader!../../../../bindings/wasm/examples/src/resolve_history.js';
import CodeSnippet from '../../../src/components/CodeSnippetComponent'
@@ -61,7 +61,7 @@ What happens in this example can be explained on a high level as follows: The Re
## Resolving from a private tangle
Resolving a DID from a private tangle is similar to resolving a DID from the main net. The only difference is that
-the resolver needs to be configured to have a client capable of operating on said private tangle. Building a `Client` configured for a specified Tangle is explained in [this example in Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples/low-level-api/private_tangle.rs) and [this example in Javascript](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples/src/private_tangle.js).
+the resolver needs to be configured to have a client capable of operating on said private tangle. Building a `Client` configured for a specified Tangle is explained in [this example in Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/low-level-api/private_tangle.rs) and [this example in Javascript](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples/src/private_tangle.js).
The following example demonstrates how one can setup a `Resolver` with a given `client` and then attempt resolving a specified `did` which may be on any Tangle (public or private).
@@ -168,7 +168,7 @@ This section shows complete examples from the Iota Identity Framework code base.
nodeReplitLink="https://repl.it/@IOTAFoundation/resolve-did?lite=true"
rustContent={resolve_did_rs}
nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples/src/resolve_did.js"
- rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/low-level-api/resolve_did.rs"
+ rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/low-level-api/resolve_did.rs"
/>
diff --git a/documentation/docs/concepts/decentralized_identifiers/update.mdx b/documentation/docs/concepts/decentralized_identifiers/update.mdx
index ae95b5e834..91bd8e2d48 100644
--- a/documentation/docs/concepts/decentralized_identifiers/update.mdx
+++ b/documentation/docs/concepts/decentralized_identifiers/update.mdx
@@ -10,7 +10,7 @@ keywords:
- Update
- Publish
---
-import account_manipulate_did_rs from '!!raw-loader!../../../../examples/account/manipulate_did.rs';
+import account_manipulate_did_rs from '!!raw-loader!../../../../examples_legacy/account/manipulate_did.rs';
import CodeSnippet from '../../../src/components/CodeSnippetComponent'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
@@ -72,7 +72,7 @@ The following example demonstrates adding verification methods and services to a
nodeReplitLink="https://repl.it/@IOTAFoundation/manipulate-did?lite=true"
rustContent={account_manipulate_did_rs}
nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/manipulate_did.ts"
- rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/manipulate_did.rs"
+ rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/manipulate_did.rs"
/>
### Creating Identity
@@ -404,5 +404,5 @@ Furthermore and similar to deleting verification methods, services can be delete
:::tip
In this example, a message is published to the tangle every time the document is updated. These messages can be unnecessary. Instead, one message can be published that contains all the updates to the DID Document.
-See the [lazy example for Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples/account/lazy.rs) and [lazy example for JS](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/lazy.ts) to learn more about lazy publishing.
+See the [lazy example for Rust](https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/lazy.rs) and [lazy example for JS](https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/lazy.ts) to learn more about lazy publishing.
:::
diff --git a/documentation/docs/concepts/verifiable_credentials/create.mdx b/documentation/docs/concepts/verifiable_credentials/create.mdx
index d4577dec46..dba0368c3f 100644
--- a/documentation/docs/concepts/verifiable_credentials/create.mdx
+++ b/documentation/docs/concepts/verifiable_credentials/create.mdx
@@ -9,7 +9,7 @@ keywords:
- Create
- sign
---
-import create_vc_rs from '!!raw-loader!../../../../examples/account/create_vc.rs';
+import create_vc_rs from '!!raw-loader!../../../../examples_legacy/account/create_vc.rs';
import CodeSnippet from '../../../src/components/CodeSnippetComponent'
A [verifiable credential (VC)](./overview.md) can represent all information that a physical credential represents, such as a passport or university degree. However, by allowing other parties to cryptographically verify the authorship and integrity of the claims, verifiable credentials can be seen as more tamper-evident and more trustworthy than their physical counterparts.
@@ -82,5 +82,5 @@ This Verifiable Credential can be [verified by anyone](./verifiable_presentation
nodeReplitLink="https://repl.it/@IOTAFoundation/create-vc?lite=true"
rustContent={create_vc_rs}
nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_vc.ts"
- rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_vc.rs"
+ rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/create_vc.rs"
/>
\ No newline at end of file
diff --git a/documentation/docs/concepts/verifiable_credentials/revocation.mdx b/documentation/docs/concepts/verifiable_credentials/revocation.mdx
index 70bab99914..0ec43bb7de 100644
--- a/documentation/docs/concepts/verifiable_credentials/revocation.mdx
+++ b/documentation/docs/concepts/verifiable_credentials/revocation.mdx
@@ -10,7 +10,7 @@ keywords:
- revocation
---
-import revoke_vc_rs from "!!raw-loader!../../../../examples/account/revoke_vc.rs";
+import revoke_vc_rs from "!!raw-loader!../../../../examples_legacy/account/revoke_vc.rs";
import CodeSnippet from "../../../src/components/CodeSnippetComponent";
## Overview
@@ -58,5 +58,5 @@ The following code exemplifies how you can revoke a [Verifiable Credential (VC)]
nodeReplitLink="https://replit.com/@IOTAFoundation/revoke-vc-06?lite=true"
rustContent={revoke_vc_rs}
nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/revoke_vc.ts"
- rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/revoke_vc.rs"
+ rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/revoke_vc.rs"
/>
diff --git a/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx b/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx
index 4c1c9e4c5c..d2a1c3fbe7 100644
--- a/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx
+++ b/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx
@@ -7,7 +7,7 @@ keywords:
- verifiable
- presentations
---
-import create_vp_rs from '!!raw-loader!../../../../examples/account/create_vp.rs';
+import create_vp_rs from '!!raw-loader!../../../../examples_legacy/account/create_vp.rs';
import CodeSnippet from '../../../src/components/CodeSnippetComponent'
A verifiable presentation is the recommended data format for sharing one or more [verifiable credentials](./overview.md).
@@ -169,5 +169,5 @@ serialize it to JSON for transmission, deserialize it on the receiving side as a
nodeReplitLink="https://repl.it/@IOTAFoundation/create-vp?lite=true"
rustContent={create_vp_rs}
nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_vp.ts"
- rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_vp.rs"
+ rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/create_vp.rs"
/>
diff --git a/documentation/docs/getting_started/create_and_publish.mdx b/documentation/docs/getting_started/create_and_publish.mdx
index 3c3ec1601b..a8a3b8281b 100644
--- a/documentation/docs/getting_started/create_and_publish.mdx
+++ b/documentation/docs/getting_started/create_and_publish.mdx
@@ -11,7 +11,7 @@ keywords:
- Publish
---
import CodeSnippet from '../../src/components/CodeSnippetComponent'
-import createDidRustExample from '!!raw-loader!../../../examples/account/create_did.rs';
+import createDidRustExample from '!!raw-loader!../../../examples_legacy/account/create_did.rs';
If you want to benefit from Self-Sovereign Identity, you need to create a [Decentralized Identity](../concepts/decentralized_identifiers/overview). This identity consists of many parts that have different functions. This page will cover the basics about identity creation and publishing to the Tangle.
@@ -33,7 +33,7 @@ Select your programming language of choice and press the green play button to ex
nodeReplitLink="https://repl.it/@IOTAFoundation/create-did?lite=true"
rustContent={createDidRustExample}
nodeGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/bindings/wasm/examples-account/src/create_did.ts"
- rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples/account/create_did.rs"
+ rustGithubLink = "https://github.com/iotaledger/identity.rs/blob/dev/examples_legacy/account/create_did.rs"
/>
The first step in this example is the creation of an account. The account is a stateful object that manages one or more identities. The account provides an interface to execute high-level operations on identities, such as [creating](../concepts/decentralized_identifiers/create) and [updating](../concepts/decentralized_identifiers/update)) them.
diff --git a/examples/0_basic/0_create_did.rs b/examples/0_basic/0_create_did.rs
new file mode 100644
index 0000000000..7ecb79740a
--- /dev/null
+++ b/examples/0_basic/0_create_did.rs
@@ -0,0 +1,64 @@
+// Copyright 2020-2022 IOTA Stiftung
+// SPDX-License-Identifier: Apache-2.0
+
+use anyhow::Context;
+use examples::get_address_with_funds;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
+use identity_core::convert::ToJson;
+use identity_core::crypto::KeyPair;
+use identity_core::crypto::KeyType;
+use identity_did::verification::MethodScope;
+use identity_stardust::NetworkName;
+use identity_stardust::StardustClientExt;
+use identity_stardust::StardustDocument;
+use identity_stardust::StardustIdentityClientExt;
+use identity_stardust::StardustVerificationMethod;
+use iota_client::block::address::Address;
+use iota_client::block::output::AliasOutput;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
+
+/// Demonstrates how to create a DID Document and publish it in a new Alias Output.
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
+ // Get an address and with funds for testing.
+ let address: Address = get_address_with_funds(&client, &mut secret_manager)
+ .await
+ .context("failed to get address with funds")?;
+
+ // Get the Bech32 human-readable part (HRP) of the network.
+ let network_name: NetworkName = client.network_name().await?;
+
+ // Create a new DID document with a placeholder DID.
+ // The DID will be derived from the Alias Id of the Alias Output after publishing.
+ let mut document: StardustDocument = StardustDocument::new(&network_name);
+
+ // Insert a new Ed25519 verification method in the DID document.
+ let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?;
+ let method: StardustVerificationMethod =
+ StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?;
+ document.insert_method(method, MethodScope::VerificationMethod)?;
+
+ // Construct an Alias Output containing the DID document, with the wallet address
+ // set as both the state controller and governor.
+ let alias_output: AliasOutput = client.new_did_output(address, document, None).await?;
+ println!("Alias Output: {}", alias_output.to_json()?);
+
+ // Publish the Alias Output and get the published DID document.
+ let document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?;
+ println!("Published DID document: {:#}", document);
+
+ Ok(())
+}
diff --git a/identity_stardust/examples/ex1_update_did.rs b/examples/0_basic/1_update_did.rs
similarity index 69%
rename from identity_stardust/examples/ex1_update_did.rs
rename to examples/0_basic/1_update_did.rs
index 77e44c6a05..d3253107f5 100644
--- a/identity_stardust/examples/ex1_update_did.rs
+++ b/examples/0_basic/1_update_did.rs
@@ -1,30 +1,43 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
+use examples::create_did;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
use identity_core::common::Timestamp;
use identity_core::convert::FromJson;
use identity_core::json;
use identity_did::did::DID;
use identity_did::service::Service;
use identity_did::verification::MethodRelationship;
-use iota_client::block::output::AliasOutput;
-use iota_client::block::output::AliasOutputBuilder;
-use iota_client::secret::SecretManager;
-use iota_client::Client;
-
+use identity_stardust::block::address::Address;
+use identity_stardust::block::output::RentStructure;
use identity_stardust::StardustClientExt;
use identity_stardust::StardustDID;
use identity_stardust::StardustDocument;
use identity_stardust::StardustIdentityClientExt;
use identity_stardust::StardustService;
-
-mod ex0_create_did;
+use iota_client::block::output::AliasOutput;
+use iota_client::block::output::AliasOutputBuilder;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
/// Demonstrates how to update a DID document in an existing Alias Output.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
// Create a new DID in an Alias Output for us to modify.
- let (client, _, secret_manager, did): (Client, _, SecretManager, StardustDID) = ex0_create_did::run().await?;
+ let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?;
// Resolve the latest state of the document.
let mut document: StardustDocument = client.resolve_did(&did).await?;
@@ -49,8 +62,8 @@ async fn main() -> anyhow::Result<()> {
// Because the size of the DID document increased, we have to increase the allocated storage deposit.
// This increases the deposit amount to the new minimum.
- let rent_structure = client.get_rent_structure().await?;
- let alias_output = AliasOutputBuilder::from(&alias_output)
+ let rent_structure: RentStructure = client.get_rent_structure().await?;
+ let alias_output: AliasOutput = AliasOutputBuilder::from(&alias_output)
.with_minimum_storage_deposit(rent_structure)
.finish()?;
diff --git a/identity_stardust/examples/ex2_resolve_did.rs b/examples/0_basic/2_resolve_did.rs
similarity index 50%
rename from identity_stardust/examples/ex2_resolve_did.rs
rename to examples/0_basic/2_resolve_did.rs
index fdfedfdb73..e3b9cca41d 100644
--- a/identity_stardust/examples/ex2_resolve_did.rs
+++ b/examples/0_basic/2_resolve_did.rs
@@ -1,19 +1,33 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-use iota_client::block::output::AliasOutput;
-use iota_client::Client;
-
+use examples::create_did;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
+use identity_stardust::block::address::Address;
use identity_stardust::StardustDID;
use identity_stardust::StardustDocument;
use identity_stardust::StardustIdentityClientExt;
-
-mod ex0_create_did;
+use iota_client::block::output::AliasOutput;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
/// Demonstrates how to resolve an existing DID in an Alias Output.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
- let (client, _, _, did): (Client, _, _, StardustDID) = ex0_create_did::run().await?;
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
+ // Create a new DID in an Alias Output for us to modify.
+ let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?;
// Resolve the associated Alias Output and extract the DID document from it.
let resolved: StardustDocument = client.resolve_did(&did).await?;
@@ -21,6 +35,7 @@ async fn main() -> anyhow::Result<()> {
// We can also resolve the Alias Output directly.
let alias_output: AliasOutput = client.resolve_did_output(&did).await?;
+
println!("The Alias Output holds {} tokens", alias_output.amount());
Ok(())
diff --git a/identity_stardust/examples/ex3_deactivate_did.rs b/examples/0_basic/3_deactivate_did.rs
similarity index 78%
rename from identity_stardust/examples/ex3_deactivate_did.rs
rename to examples/0_basic/3_deactivate_did.rs
index c9634804f5..b3b1ae2a8d 100644
--- a/identity_stardust/examples/ex3_deactivate_did.rs
+++ b/examples/0_basic/3_deactivate_did.rs
@@ -1,24 +1,35 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-use iota_client::block::address::Address;
-use iota_client::block::output::AliasOutput;
-use iota_client::block::output::AliasOutputBuilder;
-use iota_client::secret::SecretManager;
-use iota_client::Client;
-
+use examples::create_did;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
+use identity_stardust::block::address::Address;
use identity_stardust::StardustClientExt;
use identity_stardust::StardustDID;
use identity_stardust::StardustDocument;
use identity_stardust::StardustIdentityClientExt;
-
-mod ex0_create_did;
+use iota_client::block::output::AliasOutput;
+use iota_client::block::output::AliasOutputBuilder;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
/// Demonstrates how to deactivate a DID in an Alias Output.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
// Create a new DID in an Alias Output for us to modify.
- let (client, _, secret_manager, did): (Client, Address, SecretManager, StardustDID) = ex0_create_did::run().await?;
+ let (_, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?;
// Resolve the latest state of the DID document, so we can reactivate it later.
let document: StardustDocument = client.resolve_did(&did).await?;
diff --git a/identity_stardust/examples/ex4_delete_did.rs b/examples/0_basic/4_delete_did.rs
similarity index 62%
rename from identity_stardust/examples/ex4_delete_did.rs
rename to examples/0_basic/4_delete_did.rs
index 458de0b995..4312c4943c 100644
--- a/identity_stardust/examples/ex4_delete_did.rs
+++ b/examples/0_basic/4_delete_did.rs
@@ -1,23 +1,33 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-use iota_client::block::address::Address;
-use iota_client::secret::SecretManager;
-use iota_client::Client;
-
+use examples::create_did;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
use identity_stardust::Error;
use identity_stardust::StardustClientExt;
use identity_stardust::StardustDID;
use identity_stardust::StardustIdentityClientExt;
-
-mod ex0_create_did;
+use iota_client::block::address::Address;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
/// Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
// Create a new DID in an Alias Output for us to modify.
- let (client, address, secret_manager, did): (Client, Address, SecretManager, StardustDID) =
- ex0_create_did::run().await?;
+ let (address, did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?;
// Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed.
// This operation is *not* reversible.
diff --git a/examples/1_advanced/0_did_controls_did.rs b/examples/1_advanced/0_did_controls_did.rs
new file mode 100644
index 0000000000..948341c21d
--- /dev/null
+++ b/examples/1_advanced/0_did_controls_did.rs
@@ -0,0 +1,143 @@
+// Copyright 2020-2022 IOTA Stiftung
+// SPDX-License-Identifier: Apache-2.0
+
+use std::ops::Deref;
+
+use examples::create_did;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
+use identity_core::crypto::KeyPair;
+use identity_core::crypto::KeyType;
+use identity_did::verification::MethodScope;
+use identity_stardust::block::output::AliasId;
+use identity_stardust::block::output::UnlockCondition;
+use identity_stardust::NetworkName;
+use identity_stardust::StardustClientExt;
+use identity_stardust::StardustDID;
+use identity_stardust::StardustDocument;
+use identity_stardust::StardustIdentityClientExt;
+use identity_stardust::StardustVerificationMethod;
+use iota_client::block::address::Address;
+use iota_client::block::address::AliasAddress;
+use iota_client::block::output::feature::IssuerFeature;
+use iota_client::block::output::AliasOutput;
+use iota_client::block::output::AliasOutputBuilder;
+use iota_client::block::output::RentStructure;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
+
+/// Demonstrates how an identity can control another identity.
+///
+/// For this example, we consider the case where a parent company's DID controls the DID of a subsidiary.
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // ========================================================
+ // Create the company's and subsidiary's Alias Output DIDs.
+ // ========================================================
+
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
+ // Create a new DID for the company.
+ let (_, company_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?;
+
+ // Get the current byte costs and network name.
+ let rent_structure: RentStructure = client.get_rent_structure().await?;
+ let network_name: NetworkName = client.network_name().await?;
+
+ // Construct a new DID document for the subsidiary.
+ let subsidiary_document: StardustDocument = StardustDocument::new(&network_name);
+
+ // Create a DID for the subsidiary that is controlled by the parent company's DID.
+ // This means the subsidiary's Alias Output can only be updated or destroyed by
+ // the state controller or governor of the company's Alias Output respectively.
+ let subsidiary_alias: AliasOutput = client
+ .new_did_output(
+ Address::Alias(AliasAddress::new(AliasId::from(&company_did))),
+ subsidiary_document,
+ Some(rent_structure.clone()),
+ )
+ .await?;
+
+ let subsidiary_alias: AliasOutput = AliasOutputBuilder::from(&subsidiary_alias)
+ // Optionally, we can mark the company as the issuer of the subsidiary DID.
+ // This allows to verify trust relationships between DIDs, as a resolver can
+ // verify that the subsidiary DID was created by the parent company.
+ .add_immutable_feature(IssuerFeature::new(AliasAddress::new(AliasId::from(&company_did)).into()).into())
+ // Adding the issuer feature means we have to recalculate the required storage deposit.
+ .with_minimum_storage_deposit(rent_structure.clone())
+ .finish()?;
+
+ // Publish the subsidiary's DID.
+ let mut subsidiary_document: StardustDocument = client.publish_did_output(&secret_manager, subsidiary_alias).await?;
+
+ // =====================================
+ // Update the subsidiary's Alias Output.
+ // =====================================
+
+ // Add a verification method to the subsidiary.
+ // This only serves as an example for updating the subsidiary DID.
+ let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?;
+ let method: StardustVerificationMethod = StardustVerificationMethod::new(
+ subsidiary_document.id().clone(),
+ keypair.type_(),
+ keypair.public(),
+ "#key-2",
+ )?;
+ subsidiary_document.insert_method(method, MethodScope::VerificationMethod)?;
+
+ // Update the subsidiary's Alias Output with the updated document
+ // and increase the storage deposit.
+ let subsidiary_alias: AliasOutput = client.update_did_output(subsidiary_document).await?;
+ let subsidiary_alias: AliasOutput = AliasOutputBuilder::from(&subsidiary_alias)
+ .with_minimum_storage_deposit(rent_structure.clone())
+ .finish()?;
+
+ // Publish the updated subsidiary's DID.
+ //
+ // This works because `secret_manager` can unlock the company's Alias Output,
+ // which is required in order to update the subsidiary's Alias Output.
+ let subsidiary_document: StardustDocument = client.publish_did_output(&secret_manager, subsidiary_alias).await?;
+
+ // ===================================================================
+ // Determine the controlling company's DID given the subsidiary's DID.
+ // ===================================================================
+
+ // Resolve the subsidiary's Alias Output.
+ let subsidiary_output: AliasOutput = client.resolve_did_output(subsidiary_document.id()).await?;
+
+ // Extract the company's Alias Id from the state controller unlock condition.
+ //
+ // If instead we wanted to determine the original creator of the DID,
+ // we could inspect the issuer feature. This feature needs to be set when creating the DID.
+ let company_alias_id: AliasId = if let Some(UnlockCondition::StateControllerAddress(address)) =
+ subsidiary_output.unlock_conditions().iter().next()
+ {
+ if let Address::Alias(alias) = *address.address() {
+ *alias.alias_id()
+ } else {
+ anyhow::bail!("expected an alias address as the state controller");
+ }
+ } else {
+ anyhow::bail!("expected two unlock conditions");
+ };
+
+ // Reconstruct the company's DID from the Alias Id and the network.
+ let company_did = StardustDID::new(company_alias_id.deref(), &network_name);
+
+ // Resolve the company's DID document.
+ let company_document: StardustDocument = client.resolve_did(&company_did).await?;
+
+ println!("Company: {company_document:#}");
+ println!("Subsidiary: {subsidiary_document:#}");
+
+ Ok(())
+}
diff --git a/examples/1_advanced/1_did_issues_nft.rs b/examples/1_advanced/1_did_issues_nft.rs
new file mode 100644
index 0000000000..b06b6991c7
--- /dev/null
+++ b/examples/1_advanced/1_did_issues_nft.rs
@@ -0,0 +1,147 @@
+// Copyright 2020-2022 IOTA Stiftung
+// SPDX-License-Identifier: Apache-2.0
+
+use examples::create_did;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
+use identity_stardust::block::output::feature::MetadataFeature;
+use identity_stardust::NetworkName;
+use identity_stardust::StardustDID;
+use identity_stardust::StardustDocument;
+use identity_stardust::StardustIdentityClientExt;
+use iota_client::api_types::responses::OutputResponse;
+use iota_client::block::address::Address;
+use iota_client::block::address::AliasAddress;
+use iota_client::block::output::feature::IssuerFeature;
+use iota_client::block::output::unlock_condition::AddressUnlockCondition;
+use iota_client::block::output::AliasId;
+use iota_client::block::output::Feature;
+use iota_client::block::output::NftId;
+use iota_client::block::output::NftOutput;
+use iota_client::block::output::NftOutputBuilder;
+use iota_client::block::output::Output;
+use iota_client::block::output::OutputId;
+use iota_client::block::output::RentStructure;
+use iota_client::block::output::UnlockCondition;
+use iota_client::block::payload::transaction::TransactionEssence;
+use iota_client::block::payload::Payload;
+use iota_client::block::Block;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
+
+/// Demonstrates how an identity can issue and own NFTs,
+/// and how observers can verify the issuer of the NFT.
+///
+/// For this example, we consider the case where a manufacturer issues
+/// a digital product passport (DPP) as an NFT.
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // ==============================================
+ // Create the manufacturer's DID and the DPP NFT.
+ // ==============================================
+
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
+ // Create a new DID for the manufacturer.
+ let (_, manufacturer_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?;
+
+ // Get the current byte cost.
+ let rent_structure: RentStructure = client.get_rent_structure().await?;
+
+ // Create a Digital Product Passport NFT issued by the manufacturer.
+ let product_passport_nft: NftOutput =
+ NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, NftId::null())?
+ // The NFT will initially be owned by the manufacturer.
+ .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(Address::Alias(
+ AliasAddress::new(AliasId::from(&manufacturer_did)),
+ ))))
+ // Set the manufacturer as the immutable issuer.
+ .add_immutable_feature(Feature::Issuer(IssuerFeature::new(Address::Alias(AliasAddress::new(
+ AliasId::from(&manufacturer_did),
+ )))))
+ // A proper DPP would hold its metadata here.
+ .add_immutable_feature(Feature::Metadata(MetadataFeature::new(
+ b"Digital Product Passport Metadata".to_vec(),
+ )?))
+ .finish()?;
+
+ // Publish the NFT.
+ let block: Block = client
+ .block()
+ .with_secret_manager(&secret_manager)
+ .with_outputs(vec![product_passport_nft.into()])?
+ .finish()
+ .await?;
+ let _ = client.retry_until_included(&block.id(), None, None).await?;
+
+ // ========================================================
+ // Resolve the Digital Product Passport NFT and its issuer.
+ // ========================================================
+
+ // Extract the identifier of the NFT from the published block.
+ let nft_id: NftId = NftId::from(get_nft_output_id(
+ block
+ .payload()
+ .ok_or_else(|| anyhow::anyhow!("expected block to contain a payload"))?,
+ )?);
+
+ // Fetch the NFT Output.
+ let nft_output_id: OutputId = client.nft_output_id(nft_id).await?;
+ let output_response: OutputResponse = client.get_output(&nft_output_id).await?;
+ let output: Output = Output::try_from(&output_response.output)?;
+
+ // Extract the issuer of the NFT.
+ let nft_output: NftOutput = if let Output::Nft(nft_output) = output {
+ nft_output
+ } else {
+ anyhow::bail!("expected NFT output")
+ };
+
+ let issuer_address: Address = if let Some(Feature::Issuer(issuer)) = nft_output.immutable_features().iter().next() {
+ *issuer.address()
+ } else {
+ anyhow::bail!("expected an issuer feature")
+ };
+
+ let manufacturer_alias_id: AliasId = if let Address::Alias(alias_address) = issuer_address {
+ *alias_address.alias_id()
+ } else {
+ anyhow::bail!("expected an Alias Address")
+ };
+
+ // Reconstruct the manufacturer's DID from the Alias Id.
+ let network: NetworkName = client.network_name().await?;
+ let manufacturer_did: StardustDID = StardustDID::new(&*manufacturer_alias_id, &network);
+
+ // Resolve the issuer of the NFT.
+ let manufacturer_document: StardustDocument = client.resolve_did(&manufacturer_did).await?;
+
+ println!("The issuer of the Digital Product Passport NFT is: {manufacturer_document:#}");
+
+ Ok(())
+}
+
+// Helper function to get the output id for the first NFT output in a Block.
+fn get_nft_output_id(payload: &Payload) -> anyhow::Result {
+ match payload {
+ Payload::Transaction(tx_payload) => {
+ let TransactionEssence::Regular(regular) = tx_payload.essence();
+ for (index, output) in regular.outputs().iter().enumerate() {
+ if let Output::Nft(_nft_output) = output {
+ return Ok(OutputId::new(tx_payload.id(), index.try_into().unwrap())?);
+ }
+ }
+ anyhow::bail!("no NFT output in transaction essence")
+ }
+ _ => anyhow::bail!("No transaction payload"),
+ }
+}
diff --git a/examples/1_advanced/2_nft_owns_did.rs b/examples/1_advanced/2_nft_owns_did.rs
new file mode 100644
index 0000000000..d90f25f6fd
--- /dev/null
+++ b/examples/1_advanced/2_nft_owns_did.rs
@@ -0,0 +1,146 @@
+// Copyright 2020-2022 IOTA Stiftung
+// SPDX-License-Identifier: Apache-2.0
+
+use examples::create_did_document;
+use examples::get_address_with_funds;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
+use identity_stardust::block::address::NftAddress;
+use identity_stardust::block::output::AliasOutput;
+use identity_stardust::NetworkName;
+use identity_stardust::StardustClientExt;
+use identity_stardust::StardustDocument;
+use identity_stardust::StardustIdentityClientExt;
+use iota_client::api_types::responses::OutputResponse;
+use iota_client::block::address::Address;
+use iota_client::block::output::unlock_condition::AddressUnlockCondition;
+use iota_client::block::output::NftId;
+use iota_client::block::output::NftOutput;
+use iota_client::block::output::NftOutputBuilder;
+use iota_client::block::output::Output;
+use iota_client::block::output::OutputId;
+use iota_client::block::output::RentStructure;
+use iota_client::block::output::UnlockCondition;
+use iota_client::block::payload::transaction::TransactionEssence;
+use iota_client::block::payload::Payload;
+use iota_client::block::Block;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
+
+/// Demonstrates how an identity can be owned by NFTs,
+/// and how observers can verify that relationship.
+///
+/// For this example, we consider the case where a car's NFT owns
+/// the DID of the car, so that transferring the NFT also transfers DID ownership.
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // =============================
+ // Create the car's NFT and DID.
+ // =============================
+
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
+ // Get an address with funds for testing.
+ let address: Address = get_address_with_funds(&client, &mut secret_manager).await?;
+
+ // Get the current byte cost.
+ let rent_structure: RentStructure = client.get_rent_structure().await?;
+
+ // Create the car NFT with an Ed25519 address as the unlock condition.
+ let car_nft: NftOutput = NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure.clone(), NftId::null())?
+ .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(address)))
+ .finish()?;
+
+ // Publish the NFT output.
+ let block: Block = client
+ .block()
+ .with_secret_manager(&secret_manager)
+ .with_outputs(vec![car_nft.into()])?
+ .finish()
+ .await?;
+ let _ = client.retry_until_included(&block.id(), None, None).await?;
+
+ let car_nft_id: NftId = NftId::from(get_nft_output_id(
+ block
+ .payload()
+ .ok_or_else(|| anyhow::anyhow!("expected the block to contain a payload"))?,
+ )?);
+
+ let network: NetworkName = client.network_name().await?;
+
+ // Construct a DID document for the subsidiary.
+ let document: StardustDocument = create_did_document(&network)?;
+
+ // Create a new DID for the car that is owned by the car NFT.
+ let car_did_output: AliasOutput = client
+ .new_did_output(Address::Nft(car_nft_id.into()), document, Some(rent_structure))
+ .await?;
+
+ let car_document: StardustDocument = client.publish_did_output(&secret_manager, car_did_output).await?;
+
+ // ============================================
+ // Determine the car's NFT given the car's DID.
+ // ============================================
+
+ let output: AliasOutput = client.resolve_did_output(car_document.id()).await?;
+
+ // Extract the NFT address from the state controller unlock condition.
+ let unlock_condition: &UnlockCondition = output
+ .unlock_conditions()
+ .iter()
+ .next()
+ .ok_or_else(|| anyhow::anyhow!("expected at least one unlock condition"))?;
+
+ let car_nft_address: NftAddress =
+ if let UnlockCondition::StateControllerAddress(state_controller_unlock_condition) = unlock_condition {
+ if let Address::Nft(nft_address) = state_controller_unlock_condition.address() {
+ *nft_address
+ } else {
+ anyhow::bail!("expected an NFT address as the unlock condition");
+ }
+ } else {
+ anyhow::bail!("expected an Address as the unlock condition");
+ };
+
+ // Retrieve the NFT Output of the car.
+ let car_nft_id: &NftId = car_nft_address.nft_id();
+ let output_id: OutputId = client.nft_output_id(*car_nft_id).await?;
+ let output_response: OutputResponse = client.get_output(&output_id).await?;
+ let output: Output = Output::try_from(&output_response.output)?;
+
+ let car_nft: NftOutput = if let Output::Nft(nft_output) = output {
+ nft_output
+ } else {
+ anyhow::bail!("expected an NFT output");
+ };
+
+ println!("The car's DID is: {car_document:#}");
+ println!("The car's NFT is: {car_nft:#?}");
+
+ Ok(())
+}
+
+// Helper function to get the output id for the first NFT output in a Block.
+fn get_nft_output_id(payload: &Payload) -> anyhow::Result {
+ match payload {
+ Payload::Transaction(tx_payload) => {
+ let TransactionEssence::Regular(regular) = tx_payload.essence();
+ for (index, output) in regular.outputs().iter().enumerate() {
+ if let Output::Nft(_nft_output) = output {
+ return Ok(OutputId::new(tx_payload.id(), index.try_into().unwrap())?);
+ }
+ }
+ anyhow::bail!("no NFT output in transaction essence")
+ }
+ _ => anyhow::bail!("No transaction payload"),
+ }
+}
diff --git a/examples/1_advanced/3_did_issues_tokens.rs b/examples/1_advanced/3_did_issues_tokens.rs
new file mode 100644
index 0000000000..7a91ef2427
--- /dev/null
+++ b/examples/1_advanced/3_did_issues_tokens.rs
@@ -0,0 +1,190 @@
+// Copyright 2020-2022 IOTA Stiftung
+// SPDX-License-Identifier: Apache-2.0
+
+use std::ops::Deref;
+
+use examples::create_did;
+use examples::get_address;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
+use identity_core::common::Duration;
+use identity_core::common::Timestamp;
+use identity_stardust::block::output::unlock_condition::AddressUnlockCondition;
+use identity_stardust::block::output::unlock_condition::ExpirationUnlockCondition;
+use identity_stardust::block::output::BasicOutput;
+use identity_stardust::block::output::BasicOutputBuilder;
+use identity_stardust::block::output::Output;
+use identity_stardust::block::output::OutputId;
+use identity_stardust::NetworkName;
+use identity_stardust::StardustDID;
+use identity_stardust::StardustDocument;
+use identity_stardust::StardustIdentityClientExt;
+use iota_client::api_types::responses::OutputResponse;
+use iota_client::block::address::Address;
+use iota_client::block::address::AliasAddress;
+use iota_client::block::output::unlock_condition::ImmutableAliasAddressUnlockCondition;
+use iota_client::block::output::AliasId;
+use iota_client::block::output::AliasOutput;
+use iota_client::block::output::AliasOutputBuilder;
+use iota_client::block::output::FoundryId;
+use iota_client::block::output::FoundryOutput;
+use iota_client::block::output::FoundryOutputBuilder;
+use iota_client::block::output::NativeToken;
+use iota_client::block::output::RentStructure;
+use iota_client::block::output::SimpleTokenScheme;
+use iota_client::block::output::TokenId;
+use iota_client::block::output::TokenScheme;
+use iota_client::block::output::UnlockCondition;
+use iota_client::block::Block;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
+use primitive_types::U256;
+
+/// Demonstrates how an identity can issue and control native assets
+/// such as Token Foundries and NFTs.
+///
+/// For this example, we consider the case where an authority issues
+/// carbon credits that can be used to pay for carbon emissions or traded on a marketplace.
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // ===========================================
+ // Create the authority's DID and the foundry.
+ // ===========================================
+
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
+ // Create a new DID for the authority.
+
+ let (_, authority_did): (Address, StardustDID) = create_did(&client, &mut secret_manager).await?;
+
+ let rent_structure: RentStructure = client.get_rent_structure().await?;
+
+ // We want to update the foundry counter of the authority's Alias Output, so we create an
+ // updated version of the output. We pass in the previous document,
+ // because we don't want to modify it in this update.
+ let authority_document: StardustDocument = client.resolve_did(&authority_did).await?;
+ let authority_alias_output: AliasOutput = client.update_did_output(authority_document).await?;
+
+ // We will add one foundry to this Alias Output.
+ let authority_alias_output = AliasOutputBuilder::from(&authority_alias_output)
+ .with_foundry_counter(1)
+ .finish()?;
+
+ // Create a token foundry that represents carbon credits.
+ let token_scheme = TokenScheme::Simple(SimpleTokenScheme::new(
+ U256::from(500_000u32),
+ U256::from(0u8),
+ U256::from(1_000_000u32),
+ )?);
+
+ // Create the identifier of the foundry, which is partially derived from the Alias Address.
+ let foundry_id = FoundryId::build(
+ &AliasAddress::new(AliasId::from(&authority_did)),
+ 1,
+ token_scheme.kind(),
+ );
+
+ // Create the Foundry Output.
+ let carbon_credits_foundry: FoundryOutput =
+ FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_structure.clone(), 1, token_scheme)?
+ // Initially, all carbon credits are owned by the foundry.
+ .add_native_token(NativeToken::new(TokenId::from(foundry_id), U256::from(500_000u32))?)
+ // The authority is set as the immutable owner.
+ .add_unlock_condition(UnlockCondition::ImmutableAliasAddress(
+ ImmutableAliasAddressUnlockCondition::new(AliasAddress::new(AliasId::from(&authority_did))),
+ ))
+ .finish()?;
+
+ let carbon_credits_foundry_id: FoundryId = carbon_credits_foundry.id();
+
+ // Publish all outputs.
+ let block: Block = client
+ .block()
+ .with_secret_manager(&secret_manager)
+ .with_outputs(vec![authority_alias_output.into(), carbon_credits_foundry.into()])?
+ .finish()
+ .await?;
+ let _ = client.retry_until_included(&block.id(), None, None).await?;
+
+ // ===================================
+ // Resolve Foundry and its issuer DID.
+ // ===================================
+
+ // Get the latest output that contains the foundry.
+ let foundry_output_id: OutputId = client.foundry_output_id(carbon_credits_foundry_id).await?;
+ let carbon_credits_foundry: OutputResponse = client.get_output(&foundry_output_id).await?;
+ let carbon_credits_foundry: Output = Output::try_from(&carbon_credits_foundry.output)?;
+
+ let carbon_credits_foundry: FoundryOutput = if let Output::Foundry(foundry_output) = carbon_credits_foundry {
+ foundry_output
+ } else {
+ anyhow::bail!("expected foundry output")
+ };
+
+ // Get the Alias Id of the authority that issued the carbon credits foundry.
+ let authority_alias_id: &AliasId = carbon_credits_foundry.alias_address().alias_id();
+
+ // Reconstruct the DID of the authority.
+ let network: NetworkName = client.network_name().await?;
+ let authority_did: StardustDID = StardustDID::new(authority_alias_id.deref(), &network);
+
+ // Resolve the authority's DID document.
+ let authority_document: StardustDocument = client.resolve_did(&authority_did).await?;
+
+ println!("The authority's DID is: {authority_document:#}");
+
+ // =========================================================
+ // Transfer 1000 carbon credits to the address of a company.
+ // =========================================================
+
+ // Create a new address that represents the company.
+ let company_address = get_address(&client, &mut secret_manager).await?;
+
+ // Create the timestamp at which the basic output will expire.
+ let tomorrow: u32 = Timestamp::now_utc()
+ .checked_add(Duration::seconds(60 * 60 * 24))
+ .ok_or_else(|| anyhow::anyhow!("timestamp overflow"))?
+ .to_unix()
+ .try_into()
+ .map_err(|err| anyhow::anyhow!("cannot fit timestamp into u32: {err}"))?;
+
+ // Create a basic output containing our carbon credits that we'll send to the company's address.
+ let basic_output: BasicOutput = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure)?
+ .add_unlock_condition(UnlockCondition::Address(AddressUnlockCondition::new(company_address)))
+ .add_native_token(NativeToken::new(carbon_credits_foundry.token_id(), U256::from(1000))?)
+ .add_unlock_condition(UnlockCondition::Expiration(ExpirationUnlockCondition::new(
+ Address::Alias(AliasAddress::new(*authority_alias_id)),
+ tomorrow,
+ )?))
+ .finish()?;
+
+ // Reduce the carbon credits in the foundry by the amount that is sent to the company.
+ let carbon_credits_foundry = FoundryOutputBuilder::from(&carbon_credits_foundry)
+ .with_native_tokens(vec![NativeToken::new(
+ carbon_credits_foundry.token_id(),
+ U256::from(499_000u32),
+ )?])
+ .finish()?;
+
+ // Publish the output, transferring the carbon credits.
+ let block: Block = client
+ .block()
+ .with_secret_manager(&secret_manager)
+ .with_outputs(vec![basic_output.into(), carbon_credits_foundry.into()])?
+ .finish()
+ .await?;
+ let _ = client.retry_until_included(&block.id(), None, None).await?;
+
+ println!("Sent carbon credits to {}", company_address.to_bech32(network.as_ref()));
+
+ Ok(())
+}
diff --git a/examples/1_advanced/4_key_exchange.rs b/examples/1_advanced/4_key_exchange.rs
new file mode 100644
index 0000000000..a3126a6ef5
--- /dev/null
+++ b/examples/1_advanced/4_key_exchange.rs
@@ -0,0 +1,133 @@
+// Copyright 2020-2022 IOTA Stiftung
+// SPDX-License-Identifier: Apache-2.0
+
+use anyhow::Context;
+use examples::get_address_with_funds;
+use examples::random_stronghold_path;
+use examples::NETWORK_ENDPOINT;
+use identity_core::crypto::KeyPair;
+use identity_core::crypto::KeyType;
+use identity_core::crypto::X25519;
+use identity_did::verification::MethodScope;
+use identity_stardust::block::address::Address;
+use identity_stardust::block::output::AliasOutput;
+use identity_stardust::block::output::RentStructure;
+use identity_stardust::NetworkName;
+use identity_stardust::StardustClientExt;
+use identity_stardust::StardustDID;
+use identity_stardust::StardustDocument;
+use identity_stardust::StardustIdentityClientExt;
+use identity_stardust::StardustVerificationMethod;
+use iota_client::secret::stronghold::StrongholdSecretManager;
+use iota_client::secret::SecretManager;
+use iota_client::Client;
+
+/// Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents.
+///
+/// Alice and Bob want to communicate securely by encrypting their messages so only they
+/// can read them. They both publish DID Documents with X25519 public keys and use them
+/// to derive a shared secret key for encryption.
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // ==============================
+ // Create DIDs for Alice and Bob.
+ // ==============================
+
+ // Create a new client to interact with the IOTA ledger.
+ let client: Client = Client::builder().with_primary_node(NETWORK_ENDPOINT, None)?.finish()?;
+
+ // Create a new secret manager backed by a Stronghold.
+ let mut secret_manager: SecretManager = SecretManager::Stronghold(
+ StrongholdSecretManager::builder()
+ .password("secure_password")
+ .try_build(random_stronghold_path())?,
+ );
+
+ // Get an address and with funds for testing.
+ let address: Address = get_address_with_funds(&client, &mut secret_manager)
+ .await
+ .context("failed to get address with funds")?;
+
+ let network: NetworkName = client.network_name().await?;
+ let rent_structure: RentStructure = client.get_rent_structure().await?;
+
+ // Alice creates and publishes their DID Document.
+ let (alice_did, alice_x25519): (StardustDID, KeyPair) = {
+ // Create a DID Document.
+ let mut alice_document: StardustDocument = StardustDocument::new(&network);
+
+ // Insert a new X25519 KeyAgreement verification method.
+ let x25519: KeyPair = KeyPair::new(KeyType::X25519)?;
+ let method: StardustVerificationMethod =
+ StardustVerificationMethod::new(alice_document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?;
+ alice_document.insert_method(method, MethodScope::key_agreement())?;
+
+ // Publish the DID.
+ let alice_output: AliasOutput = client
+ .new_did_output(address, alice_document, Some(rent_structure.clone()))
+ .await?;
+ let alice_document: StardustDocument = client.publish_did_output(&secret_manager, alice_output).await?;
+
+ (alice_document.id().clone(), x25519)
+ };
+
+ // Bob creates and publishes their DID Document.
+ let (bob_did, bob_x25519): (StardustDID, KeyPair) = {
+ // Create a DID Document.
+ let mut bob_document: StardustDocument = StardustDocument::new(&network);
+
+ // Insert a new X25519 KeyAgreement verification method.
+ let x25519: KeyPair = KeyPair::new(KeyType::X25519)?;
+ let method: StardustVerificationMethod =
+ StardustVerificationMethod::new(bob_document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?;
+ bob_document.insert_method(method, MethodScope::key_agreement())?;
+
+ // Publish the DID.
+ let bob_output: AliasOutput = client
+ .new_did_output(address, bob_document, Some(rent_structure))
+ .await?;
+ let bob_document: StardustDocument = client.publish_did_output(&secret_manager, bob_output).await?;
+
+ (bob_document.id().clone(), x25519)
+ };
+
+ // ======================================================================
+ // Alice and Bob tell each other their DIDs. They each resolve the
+ // DID Document of the other to obtain their X25519 public key.
+ // Note that in practice, they would run this code completely separately.
+ // ======================================================================
+
+ let alice_shared_secret_key: [u8; 32] = {
+ // Alice: resolves Bob's DID Document and extracts their public key.
+ let bob_document: StardustDocument = client.resolve_did(&bob_did).await?;
+ let bob_method: &StardustVerificationMethod = bob_document
+ .core_document()
+ .resolve_method("kex-0", Some(MethodScope::key_agreement()))
+ .unwrap();
+ let bob_public_key: Vec = bob_method.data().try_decode()?;
+
+ // Compute the shared secret.
+ X25519::key_exchange(alice_x25519.private(), &bob_public_key)?
+ };
+
+ let bob_shared_secret_key: [u8; 32] = {
+ // Bob: resolves Alice's DID Document and extracts their public key.
+ let alice_document: StardustDocument = client.resolve_did(&alice_did).await?;
+ let alice_method: &StardustVerificationMethod = alice_document
+ .core_document()
+ .resolve_method("kex-0", Some(MethodScope::key_agreement()))
+ .unwrap();
+ let alice_public_key: Vec = alice_method.data().try_decode()?;
+
+ // Compute the shared secret.
+ X25519::key_exchange(bob_x25519.private(), &alice_public_key)?
+ };
+
+ // Both shared secret keys computed separately by Alice and Bob will match
+ // and can then be used to establish encrypted communications.
+ assert_eq!(alice_shared_secret_key, bob_shared_secret_key);
+
+ println!("Diffie-Hellman key exchange successful!");
+
+ Ok(())
+}
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index a1a2050f0d..26f3642972 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -1,85 +1,59 @@
[package]
name = "examples"
version = "0.6.0"
+authors = ["IOTA Stiftung"]
edition = "2021"
publish = false
-release = false
[dependencies]
-identity_iota = { path = "../identity_iota" }
-pretty_env_logger = { version = "0.4" }
-rand = { version = "0.8" }
-tokio = { version = "1.17.0", features = ["full"] }
+anyhow = "1.0.62"
+identity_core = { path = "../identity_core" }
+identity_did = { path = "../identity_did" }
+identity_stardust = { path = "../identity_stardust" }
+iota-client = { version = "2.0.0-beta.2", default-features = false, features = ["tls", "stronghold"] }
+primitive-types = "0.11.1"
+rand = "0.8.5"
+tokio = { version = "1.20.1", default-features = false, features = ["rt"] }
-[[example]]
-name = "getting_started"
-path = "getting_started.rs"
-
-[[example]]
-name = "account_create"
-path = "account/create_did.rs"
-
-[[example]]
-name = "account_config"
-path = "account/config.rs"
-
-[[example]]
-name = "account_manipulate"
-path = "account/manipulate_did.rs"
-
-[[example]]
-name = "account_lazy"
-path = "account/lazy.rs"
-
-[[example]]
-name = "account_signing"
-path = "account/signing.rs"
-
-# TODO: Temporarily disabled until iotaledger/stronghold.rs#353 is fixed.
-# [[example]]
-# name = "account_multiple"
-# path = "account/multiple_identities.rs"
-
-[[example]]
-name = "account_unchecked"
-path = "account/unchecked.rs"
+[lib]
+path = "utils/utils.rs"
[[example]]
-name = "account_encryption"
-path = "account/encryption.rs"
+path = "0_basic/0_create_did.rs"
+name = "0_create_did"
[[example]]
-name = "create_did"
-path = "low-level-api/create_did.rs"
+path = "0_basic/1_update_did.rs"
+name = "1_update_did"
[[example]]
-name = "account_create_vc"
-path = "account/create_vc.rs"
+path = "0_basic/2_resolve_did.rs"
+name = "2_resolve_did"
[[example]]
-name = "account_create_vp"
-path = "account/create_vp.rs"
+path = "0_basic/3_deactivate_did.rs"
+name = "3_deactivate_did"
[[example]]
-name = "resolve_history"
-path = "low-level-api/resolve_history.rs"
+path = "0_basic/4_delete_did.rs"
+name = "4_delete_did"
[[example]]
-name = "manipulate_did"
-path = "low-level-api/manipulate_did.rs"
+path = "1_advanced/0_did_controls_did.rs"
+name = "0_did_controls_did"
[[example]]
-name = "key_exchange"
-path = "low-level-api/key_exchange.rs"
+path = "1_advanced/1_did_issues_nft.rs"
+name = "1_did_issues_nft"
[[example]]
-name = "resolve_did"
-path = "low-level-api/resolve_did.rs"
+path = "1_advanced/2_nft_owns_did.rs"
+name = "2_nft_owns_did"
[[example]]
-name = "private_tangle"
-path = "low-level-api/private_tangle.rs"
+path = "1_advanced/3_did_issues_tokens.rs"
+name = "3_did_issues_tokens"
[[example]]
-name = "account_revoke_vc"
-path = "account/revoke_vc.rs"
+path = "1_advanced/4_key_exchange.rs"
+name = "4_key_exchange"
diff --git a/examples/README.md b/examples/README.md
index 209c028188..8ed43604d1 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,10 +1,8 @@
-![banner](./../.meta/identity_banner.png)
+![banner](./../documentation/static/img/Banner/banner_identity.svg)
+# IOTA Identity Examples
-
-## IOTA Identity Examples
-
-This folder provides code examples for you to learn how IOTA Identity can be used.
+This folder provides code examples to learn how IOTA Identity can be used.
You can run each example using
@@ -12,36 +10,32 @@ You can run each example using
cargo run --example
```
-For Instance, to run the example `getting_started`, use
+For instance, to run the example `0_create_did`, use:
```rust
-cargo run --example getting_started
+cargo run --example 0_create_did
```
-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_create](./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. |
-| 7 | [account_create_vc](account/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. |
-| 8 | [account_create_vp](account/create_vp.rs) | This example explains how to create a Verifiable Presentation from a set of credentials and sign it. |
-| 9 | [account_revoke_vc](account/revoke_vc.rs) | Removes a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. |
-| 10 | [account_multiple](./account/multiple_identities.rs) | How to create multiple identities from a builder and how to load existing identities into an account. |
-| 11 | [account_unchecked](./account/unchecked.rs) | How to update the custom properties of a DID document directly by using the account's unchecked methods. |
-| 12 | [account_encryption](./account/encryption.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange by encrypting and decrypting data with a shared key. |
-
-The following examples are available for using the low-level APIs, which provides more flexibility at the cost of complexity:
-
-| # | 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 the integration chain of a DID Document. |
-| 3 | [resolve_history](low-level-api/resolve_history.rs) | Advanced example that performs multiple updates and demonstrates how to resolve the DID Document history to view them. |
-| 4 | [resolve_did](./low-level-api/resolve_did.rs) | A basic example that shows how to retrieve information through DID Document resolution/dereferencing. |
-| 5 | [key_exchange](./low-level-api/key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. |
-| 6 | [private_tangle](./low-level-api/private_tangle.rs) | Showcases the same procedure as `create_did`, but on a private tangle - a locally running hornet node. |
+## Basic Examples
+
+The following basic CRUD (Create, Read, Update, Delete) examples are available:
+
+| Name | Information |
+| :------------------------------------------------ | :----------------------------------------------------------------------------------- |
+| [0_create_did](./0_basic/0_create_did.rs) | Demonstrates how to create a DID Document and publish it in a new Alias Output. |
+| [1_update_did](./0_basic/1_update_did.rs) | Demonstrates how to update a DID document in an existing Alias Output. |
+| [2_resolve_did](./0_basic/2_resolve_did.rs) | Demonstrates how to resolve an existing DID in an Alias Output. |
+| [3_deactivate_did](./0_basic/3_deactivate_did.rs) | Demonstrates how to deactivate a DID in an Alias Output. |
+| [4_delete_did](./0_basic/4_delete_did.rs) | Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. |
+
+## Advanced Examples
+
+The following advanced examples are available:
+
+| Name | Information |
+| :--------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- |
+| [0_did_controls_did](./1_advanced/0_did_controls_did.rs) | Demonstrates how an identity can control another identity. |
+| [1_did_issues_nft](./1_advanced/1_did_issues_nft.rs) | Demonstrates how an identity can issue and own NFTs, and how observers can verify the issuer of the NFT. |
+| [2_nft_owns_did](./1_advanced/2_nft_owns_did.rs) | Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. |
+| [3_did_issues_tokens](./1_advanced/3_did_issues_tokens.rs) | Demonstrates how an identity can issue and control native assets such as Token Foundries and NFTs. |
+| [4_key_exchange](./1_advanced/4_key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. |
diff --git a/identity_stardust/examples/ex0_create_did.rs b/examples/utils/utils.rs
similarity index 50%
rename from identity_stardust/examples/ex0_create_did.rs
rename to examples/utils/utils.rs
index 5495cc5e90..932a8b56bd 100644
--- a/identity_stardust/examples/ex0_create_did.rs
+++ b/examples/utils/utils.rs
@@ -1,80 +1,98 @@
-// Copyright 2020-2022 IOTA Stiftung
-// SPDX-License-Identifier: Apache-2.0
+use std::path::PathBuf;
use anyhow::Context;
-use identity_core::convert::ToJson;
+
use identity_core::crypto::KeyPair;
use identity_core::crypto::KeyType;
use identity_did::verification::MethodScope;
+use identity_stardust::NetworkName;
+use identity_stardust::StardustClientExt;
+use identity_stardust::StardustDID;
+use identity_stardust::StardustDocument;
+use identity_stardust::StardustIdentityClientExt;
+use identity_stardust::StardustVerificationMethod;
+
use iota_client::block::address::Address;
use iota_client::block::output::AliasOutput;
use iota_client::block::output::Output;
use iota_client::crypto::keys::bip39;
use iota_client::node_api::indexer::query_parameters::QueryParameter;
-use iota_client::secret::mnemonic::MnemonicSecretManager;
use iota_client::secret::SecretManager;
use iota_client::Client;
+use rand::distributions::DistString;
-use identity_stardust::NetworkName;
-use identity_stardust::StardustClientExt;
-use identity_stardust::StardustDID;
-use identity_stardust::StardustDocument;
-use identity_stardust::StardustIdentityClient;
-use identity_stardust::StardustIdentityClientExt;
-use identity_stardust::StardustVerificationMethod;
-
-static ENDPOINT: &str = "https://api.testnet.shimmer.network/";
-static FAUCET_URL: &str = "https://faucet.testnet.shimmer.network/api/enqueue";
+pub static NETWORK_ENDPOINT: &str = "https://api.testnet.shimmer.network/";
+pub static FAUCET_URL: &str = "https://faucet.testnet.shimmer.network/api/enqueue";
-/// Demonstrates how to create a DID Document and publish it in a new Alias Output.
-pub async fn run() -> anyhow::Result<(Client, Address, SecretManager, StardustDID)> {
- // Create a client and a wallet address with funds from the testnet faucet.
- let client: Client = Client::builder().with_primary_node(ENDPOINT, None)?.finish()?;
- let (address, secret_manager): (Address, SecretManager) = get_address_with_funds(&client)
+/// Creates a DID Document and publishes it in a new Alias Output.
+///
+/// Its functionality is equivalent to the "create DID" example
+/// and exists for convenient calling from the other examples.
+pub async fn create_did(client: &Client, secret_manager: &mut SecretManager) -> anyhow::Result<(Address, StardustDID)> {
+ let address: Address = get_address_with_funds(client, secret_manager)
.await
.context("failed to get address with funds")?;
- // Get the Bech32 human-readable part (HRP) of the network.
let network_name: NetworkName = client.network_name().await?;
- // Create a new DID document with a placeholder DID.
- // The DID will be derived from the Alias Id of the Alias Output after publishing.
- let mut document: StardustDocument = StardustDocument::new(&network_name);
+ let document: StardustDocument = create_did_document(&network_name)?;
+
+ let alias_output: AliasOutput = client.new_did_output(address, document, None).await?;
+
+ let document: StardustDocument = client.publish_did_output(secret_manager, alias_output).await?;
+
+ Ok((address, document.id().clone()))
+}
+
+/// Creates an example DID document with the given `network_name`.
+///
+/// Its functionality is equivalent to the "create DID" example
+/// and exists for convenient calling from the other examples.
+pub fn create_did_document(network_name: &NetworkName) -> anyhow::Result {
+ let mut document: StardustDocument = StardustDocument::new(network_name);
- // Insert a new Ed25519 verification method in the DID document.
let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?;
+
let method: StardustVerificationMethod =
StardustVerificationMethod::new(document.id().clone(), keypair.type_(), keypair.public(), "#key-1")?;
+
document.insert_method(method, MethodScope::VerificationMethod)?;
- // Construct an Alias Output containing the DID document, with the wallet address
- // set as both the state controller and governor.
- let alias_output: AliasOutput = client.new_did_output(address, document, None).await?;
- println!("Alias Output: {}", alias_output.to_json()?);
+ Ok(document)
+}
- // Publish the Alias Output and get the published DID document.
- let document: StardustDocument = client.publish_did_output(&secret_manager, alias_output).await?;
- println!("Published DID document: {:#}", document);
+/// Generates an address from the given [`SecretManager`] and adds funds from the testnet faucet.
+pub async fn get_address_with_funds(client: &Client, stronghold: &mut SecretManager) -> anyhow::Result {
+ let address: Address = get_address(client, stronghold).await?;
- Ok((client, address, secret_manager, document.id().clone()))
+ request_faucet_funds(client, address, client.get_bech32_hrp().await?.as_str())
+ .await
+ .context("failed to request faucet funds")?;
+
+ Ok(address)
}
-/// Creates a new address and SecretManager with funds from the testnet faucet.
-async fn get_address_with_funds(client: &Client) -> anyhow::Result<(Address, SecretManager)> {
+/// Initializes the [`SecretManager`] with a new mnemonic, if necessary,
+/// and generates an address from the given [`SecretManager`].
+pub async fn get_address(client: &Client, secret_manager: &mut SecretManager) -> anyhow::Result {
let keypair = identity_core::crypto::KeyPair::new(KeyType::Ed25519)?;
let mnemonic =
iota_client::crypto::keys::bip39::wordlist::encode(keypair.private().as_ref(), &bip39::wordlist::ENGLISH)
.map_err(|err| anyhow::anyhow!(format!("{err:?}")))?;
- let secret_manager = SecretManager::Mnemonic(MnemonicSecretManager::try_from_mnemonic(&mnemonic)?);
+ if let SecretManager::Stronghold(ref mut stronghold) = secret_manager {
+ match stronghold.store_mnemonic(mnemonic).await {
+ Ok(()) => (),
+ Err(iota_client::Error::StrongholdMnemonicAlreadyStored) => (),
+ Err(err) => anyhow::bail!(err),
+ }
+ } else {
+ anyhow::bail!("expected a `StrongholdSecretManager`");
+ }
- let address = client.get_addresses(&secret_manager).with_range(0..1).get_raw().await?[0];
- let network_hrp = client.get_network_hrp().await?;
- request_faucet_funds(client, address, &network_hrp)
- .await
- .context("failed to request faucet funds")?;
+ let address = client.get_addresses(secret_manager).with_range(0..1).get_raw().await?[0];
- Ok((address, secret_manager))
+ Ok(address)
}
/// Requests funds from the testnet faucet for the given `address`.
@@ -107,9 +125,9 @@ async fn get_address_balance(client: &Client, address: &str) -> anyhow::Result anyhow::Result anyhow::Result<()> {
- run().await.map(|_| ()).map_err(|err| {
- eprintln!("ex0_create_did error: {:#}", err);
- err
- })
+/// Creates a random stronghold path in the temporary directory, whose exact location is OS-dependent.
+pub fn random_stronghold_path() -> PathBuf {
+ let mut file = std::env::temp_dir();
+ file.push("test_strongholds");
+ file.push(rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 32));
+ file.set_extension("stronghold");
+ file.to_owned()
}
diff --git a/examples_legacy/Cargo.toml b/examples_legacy/Cargo.toml
new file mode 100644
index 0000000000..07e7ab1acd
--- /dev/null
+++ b/examples_legacy/Cargo.toml
@@ -0,0 +1,84 @@
+[package]
+name = "examples_legacy"
+version = "0.6.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+identity_iota = { path = "../identity_iota" }
+pretty_env_logger = { version = "0.4" }
+rand = { version = "0.8" }
+tokio = { version = "1.17.0", features = ["full"] }
+
+[[example]]
+name = "getting_started"
+path = "getting_started.rs"
+
+[[example]]
+name = "account_create"
+path = "account/create_did.rs"
+
+[[example]]
+name = "account_config"
+path = "account/config.rs"
+
+[[example]]
+name = "account_manipulate"
+path = "account/manipulate_did.rs"
+
+[[example]]
+name = "account_lazy"
+path = "account/lazy.rs"
+
+[[example]]
+name = "account_signing"
+path = "account/signing.rs"
+
+# TODO: Temporarily disabled until iotaledger/stronghold.rs#353 is fixed.
+# [[example]]
+# name = "account_multiple"
+# path = "account/multiple_identities.rs"
+
+[[example]]
+name = "account_unchecked"
+path = "account/unchecked.rs"
+
+[[example]]
+name = "account_encryption"
+path = "account/encryption.rs"
+
+[[example]]
+name = "create_did"
+path = "low-level-api/create_did.rs"
+
+[[example]]
+name = "account_create_vc"
+path = "account/create_vc.rs"
+
+[[example]]
+name = "account_create_vp"
+path = "account/create_vp.rs"
+
+[[example]]
+name = "resolve_history"
+path = "low-level-api/resolve_history.rs"
+
+[[example]]
+name = "manipulate_did"
+path = "low-level-api/manipulate_did.rs"
+
+[[example]]
+name = "key_exchange"
+path = "low-level-api/key_exchange.rs"
+
+[[example]]
+name = "resolve_did"
+path = "low-level-api/resolve_did.rs"
+
+[[example]]
+name = "private_tangle"
+path = "low-level-api/private_tangle.rs"
+
+[[example]]
+name = "account_revoke_vc"
+path = "account/revoke_vc.rs"
diff --git a/examples_legacy/README.md b/examples_legacy/README.md
new file mode 100644
index 0000000000..c0bd008e9c
--- /dev/null
+++ b/examples_legacy/README.md
@@ -0,0 +1,47 @@
+![banner](./../documentation/static/img/Banner/banner_identity.svg)
+
+
+
+## IOTA Identity Examples
+
+This folder provides code examples for you to learn how IOTA Identity can be used.
+
+You can run each example using
+
+```rust
+cargo run --example
+```
+
+For Instance, to run the example `getting_started`, use
+
+```rust
+cargo run --example getting_started
+```
+
+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_create](./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. |
+| 7 | [account_create_vc](account/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. |
+| 8 | [account_create_vp](account/create_vp.rs) | This example explains how to create a Verifiable Presentation from a set of credentials and sign it. |
+| 9 | [account_revoke_vc](account/revoke_vc.rs) | Removes a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. |
+| 10 | [account_multiple](./account/multiple_identities.rs) | How to create multiple identities from a builder and how to load existing identities into an account. |
+| 11 | [account_unchecked](./account/unchecked.rs) | How to update the custom properties of a DID document directly by using the account's unchecked methods. |
+| 12 | [account_encryption](./account/encryption.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange by encrypting and decrypting data with a shared key. |
+
+The following examples are available for using the low-level APIs, which provides more flexibility at the cost of complexity:
+
+| # | 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 the integration chain of a DID Document. |
+| 3 | [resolve_history](low-level-api/resolve_history.rs) | Advanced example that performs multiple updates and demonstrates how to resolve the DID Document history to view them. |
+| 4 | [resolve_did](./low-level-api/resolve_did.rs) | A basic example that shows how to retrieve information through DID Document resolution/dereferencing. |
+| 5 | [key_exchange](./low-level-api/key_exchange.rs) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. |
+| 6 | [private_tangle](./low-level-api/private_tangle.rs) | Showcases the same procedure as `create_did`, but on a private tangle - a locally running hornet node. |
diff --git a/examples/account/config.rs b/examples_legacy/account/config.rs
similarity index 100%
rename from examples/account/config.rs
rename to examples_legacy/account/config.rs
diff --git a/examples/account/create_did.rs b/examples_legacy/account/create_did.rs
similarity index 100%
rename from examples/account/create_did.rs
rename to examples_legacy/account/create_did.rs
diff --git a/examples/account/create_vc.rs b/examples_legacy/account/create_vc.rs
similarity index 100%
rename from examples/account/create_vc.rs
rename to examples_legacy/account/create_vc.rs
diff --git a/examples/account/create_vp.rs b/examples_legacy/account/create_vp.rs
similarity index 100%
rename from examples/account/create_vp.rs
rename to examples_legacy/account/create_vp.rs
diff --git a/examples/account/encryption.rs b/examples_legacy/account/encryption.rs
similarity index 100%
rename from examples/account/encryption.rs
rename to examples_legacy/account/encryption.rs
diff --git a/examples/account/lazy.rs b/examples_legacy/account/lazy.rs
similarity index 100%
rename from examples/account/lazy.rs
rename to examples_legacy/account/lazy.rs
diff --git a/examples/account/manipulate_did.rs b/examples_legacy/account/manipulate_did.rs
similarity index 100%
rename from examples/account/manipulate_did.rs
rename to examples_legacy/account/manipulate_did.rs
diff --git a/examples/account/multiple_identities.rs b/examples_legacy/account/multiple_identities.rs
similarity index 100%
rename from examples/account/multiple_identities.rs
rename to examples_legacy/account/multiple_identities.rs
diff --git a/examples/account/revoke_vc.rs b/examples_legacy/account/revoke_vc.rs
similarity index 100%
rename from examples/account/revoke_vc.rs
rename to examples_legacy/account/revoke_vc.rs
diff --git a/examples/account/signing.rs b/examples_legacy/account/signing.rs
similarity index 100%
rename from examples/account/signing.rs
rename to examples_legacy/account/signing.rs
diff --git a/examples/account/unchecked.rs b/examples_legacy/account/unchecked.rs
similarity index 100%
rename from examples/account/unchecked.rs
rename to examples_legacy/account/unchecked.rs
diff --git a/examples/getting_started.rs b/examples_legacy/getting_started.rs
similarity index 100%
rename from examples/getting_started.rs
rename to examples_legacy/getting_started.rs
diff --git a/examples/low-level-api/common.rs b/examples_legacy/low-level-api/common.rs
similarity index 100%
rename from examples/low-level-api/common.rs
rename to examples_legacy/low-level-api/common.rs
diff --git a/examples/low-level-api/create_did.rs b/examples_legacy/low-level-api/create_did.rs
similarity index 100%
rename from examples/low-level-api/create_did.rs
rename to examples_legacy/low-level-api/create_did.rs
diff --git a/examples/low-level-api/key_exchange.rs b/examples_legacy/low-level-api/key_exchange.rs
similarity index 100%
rename from examples/low-level-api/key_exchange.rs
rename to examples_legacy/low-level-api/key_exchange.rs
diff --git a/examples/low-level-api/manipulate_did.rs b/examples_legacy/low-level-api/manipulate_did.rs
similarity index 100%
rename from examples/low-level-api/manipulate_did.rs
rename to examples_legacy/low-level-api/manipulate_did.rs
diff --git a/examples/low-level-api/private_tangle.rs b/examples_legacy/low-level-api/private_tangle.rs
similarity index 100%
rename from examples/low-level-api/private_tangle.rs
rename to examples_legacy/low-level-api/private_tangle.rs
diff --git a/examples/low-level-api/resolve_did.rs b/examples_legacy/low-level-api/resolve_did.rs
similarity index 100%
rename from examples/low-level-api/resolve_did.rs
rename to examples_legacy/low-level-api/resolve_did.rs
diff --git a/examples/low-level-api/resolve_history.rs b/examples_legacy/low-level-api/resolve_history.rs
similarity index 100%
rename from examples/low-level-api/resolve_history.rs
rename to examples_legacy/low-level-api/resolve_history.rs
diff --git a/identity_iota/README.md b/identity_iota/README.md
index 4fd9e6422d..00ad3729f3 100644
--- a/identity_iota/README.md
+++ b/identity_iota/README.md
@@ -1,4 +1,4 @@
-![banner](https://github.com/iotaledger/identity.rs/raw/HEAD/.meta/identity_banner.png)
+![banner](https://github.com/iotaledger/identity.rs/raw/HEAD/documentation/static/img/Banner/banner_identity.svg)
diff --git a/identity_stardust/Cargo.toml b/identity_stardust/Cargo.toml
index 371a11f59c..b94ff8faf9 100644
--- a/identity_stardust/Cargo.toml
+++ b/identity_stardust/Cargo.toml
@@ -10,16 +10,16 @@ readme = "../README.md"
repository = "https://github.com/iotaledger/identity.rs"
rust-version = "1.62"
description = "An IOTA Ledger integration for the identity.rs library."
-[workspace]
[dependencies]
# Ensure bee-block always matches the version used by iota-client.
-bee-block = { version = "1.0.0-beta.5", default-features = false, features = ["std"], optional = true }
+bee-block = { version = "1.0.0-beta.6", default-features = false, features = ["std"], optional = true }
identity_core = { version = "=0.6.0", path = "../identity_core", default-features = false }
identity_credential = { version = "=0.6.0", path = "../identity_credential", default-features = false }
identity_did = { version = "=0.6.0", path = "../identity_did", default-features = false }
async-trait = { version = "0.1.56", default-features = false, optional = true }
+iota-client = { version = "2.0.0-beta.2", default-features = false, features = ["tls"], optional = true }
num-derive = { version = "0.3", default-features = false }
num-traits = { version = "0.2", default-features = false, features = ["std"] }
once_cell = { version = "1", default-features = false, features = ["std"] }
@@ -28,13 +28,6 @@ serde = { version = "1.0", default-features = false, features = ["std", "derive"
strum = { version = "0.21", features = ["derive"] }
thiserror = { version = "1.0", default-features = false }
-[dependencies.iota-client]
-git = "https://github.com/iotaledger/iota.rs"
-rev = "a582bfa882793fe21db2055c4f7878ebc531877a" # develop branch, 2022-07-27
-features = ["tls"]
-default-features = false
-optional = true
-
[dev-dependencies]
anyhow = { version = "1.0.57" }
iota-crypto = { version = "0.12.1", default-features = false, features = ["bip39", "bip39-en"] }
diff --git a/identity_stardust/src/client/identity_client.rs b/identity_stardust/src/client/identity_client.rs
index eea778cccb..cbed527097 100644
--- a/identity_stardust/src/client/identity_client.rs
+++ b/identity_stardust/src/client/identity_client.rs
@@ -1,8 +1,6 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
-use identity_did::did::DIDError;
-
use crate::block::address::Address;
use crate::block::output::feature::SenderFeature;
use crate::block::output::unlock_condition::GovernorAddressUnlockCondition;
@@ -20,16 +18,6 @@ use crate::Result;
use crate::StardustDID;
use crate::StardustDocument;
-impl TryFrom<&StardustDID> for AliasId {
- type Error = Error;
-
- fn try_from(did: &StardustDID) -> std::result::Result {
- let tag_bytes: [u8; StardustDID::TAG_BYTES_LEN] =
- prefix_hex::decode(did.tag()).map_err(|_| DIDError::InvalidMethodId)?;
- Ok(AliasId::new(tag_bytes))
- }
-}
-
/// Helper functions necessary for the [`StardustIdentityClientExt`] trait.
#[async_trait::async_trait(? Send)]
pub trait StardustIdentityClient {
@@ -103,7 +91,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient {
///
/// Returns `Err` when failing to resolve the DID contained in `document`.
async fn update_did_output(&self, document: StardustDocument) -> Result {
- let id: AliasId = AliasId::try_from(document.id())?;
+ let id: AliasId = AliasId::from(document.id());
let (_, alias_output) = self.get_alias_output(id).await?;
let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output)
@@ -130,7 +118,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient {
///
/// Returns `Err` when failing to resolve the `did`.
async fn deactivate_did_output(&self, did: &StardustDID) -> Result {
- let alias_id: AliasId = AliasId::try_from(did)?;
+ let alias_id: AliasId = AliasId::from(did);
let (_, alias_output) = self.get_alias_output(alias_id).await?;
let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output)
@@ -154,7 +142,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient {
async fn resolve_did(&self, did: &StardustDID) -> Result {
validate_network(self, did).await?;
- let id: AliasId = AliasId::try_from(did)?;
+ let id: AliasId = AliasId::from(did);
let (_, alias_output) = self.get_alias_output(id).await?;
let document: &[u8] = alias_output.state_metadata();
@@ -170,7 +158,7 @@ pub trait StardustIdentityClientExt: StardustIdentityClient {
async fn resolve_did_output(&self, did: &StardustDID) -> Result {
validate_network(self, did).await?;
- let id: AliasId = AliasId::try_from(did)?;
+ let id: AliasId = AliasId::from(did);
self.get_alias_output(id).await.map(|(_, alias_output)| alias_output)
}
diff --git a/identity_stardust/src/client/iota_client.rs b/identity_stardust/src/client/iota_client.rs
index fb76512f6b..d3c6dcd80d 100644
--- a/identity_stardust/src/client/iota_client.rs
+++ b/identity_stardust/src/client/iota_client.rs
@@ -79,7 +79,7 @@ impl StardustClientExt for Client {
async fn delete_did_output(&self, secret_manager: &SecretManager, address: Address, did: &StardustDID) -> Result<()> {
validate_network(self, did).await?;
- let alias_id: AliasId = AliasId::try_from(did)?;
+ let alias_id: AliasId = AliasId::from(did);
let (output_id, alias_output) = self.get_alias_output(alias_id).await?;
let basic_output = BasicOutputBuilder::new_with_amount(alias_output.amount())
diff --git a/identity_stardust/src/did/stardust_did.rs b/identity_stardust/src/did/stardust_did.rs
index b18429ead5..61bc819ac3 100644
--- a/identity_stardust/src/did/stardust_did.rs
+++ b/identity_stardust/src/did/stardust_did.rs
@@ -343,6 +343,21 @@ impl KeyComparable for StardustDID {
}
}
+#[cfg(feature = "client")]
+mod __stardust_did_iota_client {
+ use crate::block::output::AliasId;
+ use crate::StardustDID;
+
+ impl From<&StardustDID> for AliasId {
+ /// Creates an [`AliasId`] from the DID tag.
+ fn from(did: &StardustDID) -> Self {
+ let tag_bytes: [u8; StardustDID::TAG_BYTES_LEN] = prefix_hex::decode(did.tag())
+ .expect("being able to successfully decode the tag should be checked during DID creation");
+ AliasId::new(tag_bytes)
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use once_cell::sync::Lazy;