From 00c04a4cc6cad3d5658f18bf7a3700f43da5be99 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 21 Nov 2022 10:12:26 +0100 Subject: [PATCH 1/6] Make StateMetadata public (#1085) --- identity_iota_core/src/state_metadata/document.rs | 4 ++-- identity_iota_core/src/state_metadata/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/identity_iota_core/src/state_metadata/document.rs b/identity_iota_core/src/state_metadata/document.rs index 872a8ffc94..9e95f7ee27 100644 --- a/identity_iota_core/src/state_metadata/document.rs +++ b/identity_iota_core/src/state_metadata/document.rs @@ -29,8 +29,8 @@ const DID_MARKER: &[u8] = b"DID"; /// an Alias Output. /// /// DID instances in the document are replaced by the `PLACEHOLDER_DID`. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub(crate) struct StateMetadataDocument { +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct StateMetadataDocument { #[serde(rename = "doc")] pub(crate) document: CoreDocument, #[serde(rename = "meta")] diff --git a/identity_iota_core/src/state_metadata/mod.rs b/identity_iota_core/src/state_metadata/mod.rs index 78aefda493..c6bec00b26 100644 --- a/identity_iota_core/src/state_metadata/mod.rs +++ b/identity_iota_core/src/state_metadata/mod.rs @@ -5,6 +5,6 @@ mod document; mod encoding; mod version; -pub(crate) use document::*; +pub use document::*; pub use encoding::*; pub(crate) use version::*; From 77df8cede96ab786ac8a283729e95b46f7fc058a Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Mon, 21 Nov 2022 11:16:34 +0100 Subject: [PATCH 2/6] Remove legacy crates (#1080) * Remove `identity_agent` dep on `identity_account` * Remove `identity_account` and most of `_storage` * Remove legacy examples * Reorganize crates included in root Cargo.toml * Remove Wasm Account * Remove CI references to account/examples_legacy * Update documentation with new examples * Temporarily add Cargo.lock file for CI to work * Deactivate stronghold-nodejs CI checks * Format toml * Add `'static` lifetime to `PLACEHOLDER_TAG` * Delete lock file, disable did-key example * Remove unneeded CI script parts --- .../actions/release/bump-versions/action.yml | 92 +-- .github/workflows/clippy.yml | 13 +- .github/workflows/format.yml | 11 +- Cargo.toml | 5 +- bindings/stronghold-nodejs/Cargo.lock | 629 +-------------- bindings/stronghold-nodejs/Cargo.toml | 2 +- .../wasm/src/account/identity/chain_state.rs | 23 - bindings/wasm/src/account/identity/mod.rs | 6 - bindings/wasm/src/account/storage/mod.rs | 7 - .../wasm/src/account/storage/test_suite.rs | 73 -- bindings/wasm/src/account/storage/traits.rs | 422 ---------- bindings/wasm/src/account/types/auto_save.rs | 38 - bindings/wasm/src/account/types/did_type.rs | 28 - .../wasm/src/account/types/identity_setup.rs | 38 - .../wasm/src/account/types/key_location.rs | 75 -- .../wasm/src/account/types/method_content.rs | 99 --- bindings/wasm/src/account/types/mod.rs | 25 - bindings/wasm/src/account/types/signature.rs | 32 - .../wasm/src/account/wasm_account/account.rs | 470 ----------- .../account/wasm_account/account_builder.rs | 161 ---- bindings/wasm/src/account/wasm_account/mod.rs | 10 - .../update/attach_method_relationships.rs | 93 --- .../wasm_account/update/create_method.rs | 100 --- .../wasm_account/update/create_service.rs | 105 --- .../wasm_account/update/delete_method.rs | 62 -- .../wasm_account/update/delete_service.rs | 64 -- .../update/detach_method_relationships.rs | 90 --- .../src/account/wasm_account/update/mod.rs | 11 - .../wasm_account/update/set_also_known_as.rs | 72 -- .../wasm_account/update/set_controller.rs | 86 -- .../verifiable_credentials/create.mdx | 9 +- .../verifiable_credentials/revocation.mdx | 10 +- .../verifiable_presentations.mdx | 9 +- examples_legacy/Cargo.toml | 90 --- examples_legacy/README.md | 47 -- examples_legacy/account/config.rs | 73 -- examples_legacy/account/create_did.rs | 51 -- examples_legacy/account/create_vc.rs | 102 --- examples_legacy/account/create_vp.rs | 191 ----- examples_legacy/account/encryption.rs | 101 --- examples_legacy/account/lazy.rs | 80 -- examples_legacy/account/manipulate_did.rs | 92 --- .../account/multiple_identities.rs | 86 -- examples_legacy/account/revoke_vc.rs | 161 ---- examples_legacy/account/signing.rs | 109 --- examples_legacy/account/unchecked.rs | 73 -- examples_legacy/getting_started.rs | 31 - examples_legacy/low-level-api/common.rs | 74 -- examples_legacy/low-level-api/create_did.rs | 53 -- examples_legacy/low-level-api/key_exchange.rs | 107 --- .../low-level-api/manipulate_did.rs | 76 -- .../low-level-api/private_tangle.rs | 79 -- examples_legacy/low-level-api/resolve_did.rs | 42 - .../low-level-api/resolve_history.rs | 151 ---- identity_account/Cargo.toml | 44 - identity_account/README.md | 42 - identity_account/src/account/account.rs | 687 ---------------- identity_account/src/account/builder.rs | 184 ----- identity_account/src/account/config.rs | 107 --- identity_account/src/account/mod.rs | 14 - .../src/account/publish_options.rs | 52 -- identity_account/src/error.rs | 48 -- identity_account/src/lib.rs | 29 - identity_account/src/tests/account.rs | 652 --------------- identity_account/src/tests/mod.rs | 6 - identity_account/src/tests/updates.rs | 754 ------------------ identity_account/src/tests/util.rs | 53 -- identity_account/src/types/identity_setup.rs | 31 - identity_account/src/types/identity_state.rs | 58 -- .../src/types/identity_updater.rs | 26 - identity_account/src/types/method_content.rs | 67 -- identity_account/src/types/mod.rs | 12 - identity_account/src/updates/error.rs | 26 - identity_account/src/updates/macros.rs | 93 --- identity_account/src/updates/mod.rs | 11 - identity_account/src/updates/update.rs | 388 --------- identity_account_storage/Cargo.toml | 53 -- identity_account_storage/README.md | 11 - identity_account_storage/src/crypto/mod.rs | 6 - identity_account_storage/src/crypto/remote.rs | 96 --- identity_account_storage/src/error.rs | 69 -- .../src/identity/chain_state.rs | 67 -- identity_account_storage/src/identity/mod.rs | 6 - identity_account_storage/src/lib.rs | 30 - .../src/storage/memstore.rs | 602 -------------- identity_account_storage/src/storage/mod.rs | 16 - .../src/storage/stronghold.rs | 731 ----------------- .../src/storage/test_suite.rs | 571 ------------- .../src/storage/traits.rs | 162 ---- .../src/types/did_type.rs | 9 - .../src/types/key_location.rs | 143 ---- identity_account_storage/src/types/mod.rs | 14 - .../src/types/signature.rs | 27 - identity_account_storage/src/utils/fs.rs | 16 - identity_account_storage/src/utils/mod.rs | 10 - identity_account_storage/src/utils/shared.rs | 37 - identity_agent/Cargo.toml | 7 +- identity_agent/benches/agent.rs | 85 +- identity_agent/src/tests/handler.rs | 4 +- .../src/tests/remote_account/error.rs | 10 +- .../src/tests/remote_account/handler.rs | 45 +- .../src/tests/remote_account/requests.rs | 19 +- .../src/tests/remote_account/tests.rs | 23 +- identity_iota_core/src/did/iota_did.rs | 39 +- 104 files changed, 184 insertions(+), 10947 deletions(-) delete mode 100644 bindings/wasm/src/account/identity/chain_state.rs delete mode 100644 bindings/wasm/src/account/identity/mod.rs delete mode 100644 bindings/wasm/src/account/storage/mod.rs delete mode 100644 bindings/wasm/src/account/storage/test_suite.rs delete mode 100644 bindings/wasm/src/account/storage/traits.rs delete mode 100644 bindings/wasm/src/account/types/auto_save.rs delete mode 100644 bindings/wasm/src/account/types/did_type.rs delete mode 100644 bindings/wasm/src/account/types/identity_setup.rs delete mode 100644 bindings/wasm/src/account/types/key_location.rs delete mode 100644 bindings/wasm/src/account/types/method_content.rs delete mode 100644 bindings/wasm/src/account/types/mod.rs delete mode 100644 bindings/wasm/src/account/types/signature.rs delete mode 100644 bindings/wasm/src/account/wasm_account/account.rs delete mode 100644 bindings/wasm/src/account/wasm_account/account_builder.rs delete mode 100644 bindings/wasm/src/account/wasm_account/mod.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/attach_method_relationships.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/create_method.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/create_service.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/delete_method.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/delete_service.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/detach_method_relationships.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/mod.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/set_also_known_as.rs delete mode 100644 bindings/wasm/src/account/wasm_account/update/set_controller.rs delete mode 100644 examples_legacy/Cargo.toml delete mode 100644 examples_legacy/README.md delete mode 100644 examples_legacy/account/config.rs delete mode 100644 examples_legacy/account/create_did.rs delete mode 100644 examples_legacy/account/create_vc.rs delete mode 100644 examples_legacy/account/create_vp.rs delete mode 100644 examples_legacy/account/encryption.rs delete mode 100644 examples_legacy/account/lazy.rs delete mode 100644 examples_legacy/account/manipulate_did.rs delete mode 100644 examples_legacy/account/multiple_identities.rs delete mode 100644 examples_legacy/account/revoke_vc.rs delete mode 100644 examples_legacy/account/signing.rs delete mode 100644 examples_legacy/account/unchecked.rs delete mode 100644 examples_legacy/getting_started.rs delete mode 100644 examples_legacy/low-level-api/common.rs delete mode 100644 examples_legacy/low-level-api/create_did.rs delete mode 100644 examples_legacy/low-level-api/key_exchange.rs delete mode 100644 examples_legacy/low-level-api/manipulate_did.rs delete mode 100644 examples_legacy/low-level-api/private_tangle.rs delete mode 100644 examples_legacy/low-level-api/resolve_did.rs delete mode 100644 examples_legacy/low-level-api/resolve_history.rs delete mode 100644 identity_account/Cargo.toml delete mode 100644 identity_account/README.md delete mode 100644 identity_account/src/account/account.rs delete mode 100644 identity_account/src/account/builder.rs delete mode 100644 identity_account/src/account/config.rs delete mode 100644 identity_account/src/account/mod.rs delete mode 100644 identity_account/src/account/publish_options.rs delete mode 100644 identity_account/src/error.rs delete mode 100644 identity_account/src/lib.rs delete mode 100644 identity_account/src/tests/account.rs delete mode 100644 identity_account/src/tests/mod.rs delete mode 100644 identity_account/src/tests/updates.rs delete mode 100644 identity_account/src/tests/util.rs delete mode 100644 identity_account/src/types/identity_setup.rs delete mode 100644 identity_account/src/types/identity_state.rs delete mode 100644 identity_account/src/types/identity_updater.rs delete mode 100644 identity_account/src/types/method_content.rs delete mode 100644 identity_account/src/types/mod.rs delete mode 100644 identity_account/src/updates/error.rs delete mode 100644 identity_account/src/updates/macros.rs delete mode 100644 identity_account/src/updates/mod.rs delete mode 100644 identity_account/src/updates/update.rs delete mode 100644 identity_account_storage/Cargo.toml delete mode 100644 identity_account_storage/README.md delete mode 100644 identity_account_storage/src/crypto/mod.rs delete mode 100644 identity_account_storage/src/crypto/remote.rs delete mode 100644 identity_account_storage/src/error.rs delete mode 100644 identity_account_storage/src/identity/chain_state.rs delete mode 100644 identity_account_storage/src/identity/mod.rs delete mode 100644 identity_account_storage/src/lib.rs delete mode 100644 identity_account_storage/src/storage/memstore.rs delete mode 100644 identity_account_storage/src/storage/mod.rs delete mode 100644 identity_account_storage/src/storage/stronghold.rs delete mode 100644 identity_account_storage/src/storage/test_suite.rs delete mode 100644 identity_account_storage/src/storage/traits.rs delete mode 100644 identity_account_storage/src/types/did_type.rs delete mode 100644 identity_account_storage/src/types/key_location.rs delete mode 100644 identity_account_storage/src/types/mod.rs delete mode 100644 identity_account_storage/src/types/signature.rs delete mode 100644 identity_account_storage/src/utils/fs.rs delete mode 100644 identity_account_storage/src/utils/mod.rs delete mode 100644 identity_account_storage/src/utils/shared.rs diff --git a/.github/actions/release/bump-versions/action.yml b/.github/actions/release/bump-versions/action.yml index 1b1543dbd8..63f34214c9 100644 --- a/.github/actions/release/bump-versions/action.yml +++ b/.github/actions/release/bump-versions/action.yml @@ -45,50 +45,6 @@ runs: run: | cargo set-version ${{ inputs.version }} - # cargo workspaces ignores examples_legacy/ but cargo release still tries to version it during publishing. - - name: Bump Rust examples_legacy version - shell: bash - if: ${{inputs.release-target == 'rust'}} - working-directory: examples_legacy - run: | - cargo set-version ${{ inputs.version }} - - # cargo workspaces ignores the legacy crates because they are excluded from the workspace. - # Bump versions for consistency. - # - name: Bump Rust legacy crates' version - # shell: bash - # if: ${{inputs.release-target == 'rust'}} - # run: | - # cargo set-version --manifest-path identity_account_storage/Cargo.toml ${{ inputs.version }} - # cargo set-version --manifest-path identity_iota_client_legacy/Cargo.toml ${{ inputs.version }} - # cargo set-version --manifest-path identity_iota_core_legacy/Cargo.toml ${{ inputs.version }} - # cargo set-version --manifest-path identity_account/Cargo.toml ${{ inputs.version }} - # cargo add --manifest-path identity_account_storage/Cargo.toml --path identity_core - # cargo add --manifest-path identity_account_storage/Cargo.toml --path identity_did - # cargo add --manifest-path identity_account_storage/Cargo.toml --path identity_iota_core_legacy - # cargo add --manifest-path identity_account/Cargo.toml --path identity_account_storage - # cargo add --manifest-path identity_account/Cargo.toml --path identity_core - # cargo add --manifest-path identity_account/Cargo.toml --path identity_credential - # cargo add --manifest-path identity_account/Cargo.toml --path identity_did - # cargo add --manifest-path identity_account/Cargo.toml --path identity_iota_client_legacy - # cargo add --manifest-path identity_account/Cargo.toml --path identity_iota_core_legacy - # cargo add --manifest-path identity_iota_core_legacy/Cargo.toml --path identity_core - # cargo add --manifest-path identity_iota_core_legacy/Cargo.toml --path identity_did - # cargo add --manifest-path identity_iota_client_legacy/Cargo.toml --path identity_core - # cargo add --manifest-path identity_iota_client_legacy/Cargo.toml --path identity_credential - # cargo add --manifest-path identity_iota_client_legacy/Cargo.toml --path identity_did - # cargo add --manifest-path identity_iota_client_legacy/Cargo.toml --path identity_iota_core_legacy - - # cargo workspaces ignores identity_agent because it is excluded from the workspace. - # Bump versions for consistency. - # - name: Bump Rust agent version - # shell: bash - # if: ${{inputs.release-target == 'rust'}} - # working-directory: identity_agent - # run: | - # cargo set-version ${{ inputs.version }} - # cargo add --path ../identity_core - - name: Bump Wasm bindings crate version shell: bash if: ${{inputs.release-target == 'wasm'}} @@ -96,12 +52,13 @@ runs: run: | cargo workspaces version --force "*" --no-git-commit --exact custom ${{ inputs.version }} -y -a - - name: Bump stronghold nodejs bindings crate version - shell: bash - if: ${{inputs.release-target == 'wasm'}} - working-directory: bindings/stronghold-nodejs - run: | - cargo set-version ${{ inputs.version }} + # Deactivated while the crate is broken, which is the case due to the removal of identity_account_storage. + # - name: Bump stronghold nodejs bindings crate version + # shell: bash + # if: ${{inputs.release-target == 'wasm'}} + # working-directory: bindings/stronghold-nodejs + # run: | + # cargo set-version ${{ inputs.version }} - name: Replace identity_iota version in Wasm bindings shell: bash @@ -110,15 +67,15 @@ runs: run: | cargo add identity_iota --path=../../identity_iota - - name: Replace identity versions in stronghold nodejs bindings - shell: bash - if: ${{inputs.release-target == 'rust'}} - working-directory: bindings/stronghold-nodejs - run: | - cargo add identity_core --path=../../identity_core - cargo add identity_did --path=../../identity_did - cargo add identity_iota_core_legacy --path=../../identity_iota_core_legacy - cargo add identity_account_storage --path=../../identity_account_storage + # Deactivated while the crate is broken, which is the case due to the removal of identity_account_storage. + # - name: Replace identity versions in stronghold nodejs bindings + # shell: bash + # if: ${{inputs.release-target == 'rust'}} + # working-directory: bindings/stronghold-nodejs + # run: | + # cargo add identity_core --path=../../identity_core + # cargo add identity_did --path=../../identity_did + # cargo add identity_iota_core_legacy --path=../../identity_iota_core_legacy - name: Set up Node.js uses: actions/setup-node@v2 @@ -134,11 +91,12 @@ runs: run: | npm version ${{ inputs.version }} - - name: Bump stronghold nodejs npm package version - shell: bash - if: ${{inputs.release-target == 'wasm'}} - working-directory: bindings/stronghold-nodejs - run: | - npm version ${{ inputs.version }} - # set peer dependency version - echo "`jq '.peerDependencies."@iota/identity-wasm"="${{ inputs.version }}"' package.json`" > package.json + # Deactivated while the crate is broken, which is the case due to the removal of identity_account_storage. + # - name: Bump stronghold nodejs npm package version + # shell: bash + # if: ${{inputs.release-target == 'wasm'}} + # working-directory: bindings/stronghold-nodejs + # run: | + # npm version ${{ inputs.version }} + # # set peer dependency version + # echo "`jq '.peerDependencies."@iota/identity-wasm"="${{ inputs.version }}"' package.json`" > package.json diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 4e7ff0956b..7030bef5d6 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -48,12 +48,13 @@ jobs: args: --manifest-path ./bindings/wasm/Cargo.toml --target wasm32-unknown-unknown --all-targets --all-features -- -D warnings name: wasm - - name: stronghold-nodejs clippy check - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --manifest-path ./bindings/stronghold-nodejs/Cargo.toml --all-targets --all-features -- -D warnings - name: stronghold-nodejs + # Deactivated while the crate is broken, which is the case due to the removal of identity_account_storage. + # - name: stronghold-nodejs clippy check + # uses: actions-rs/clippy-check@v1 + # with: + # token: ${{ secrets.GITHUB_TOKEN }} + # args: --manifest-path ./bindings/stronghold-nodejs/Cargo.toml --all-targets --all-features -- -D warnings + # name: stronghold-nodejs - name: libjose clippy check uses: actions-rs/clippy-check@v1 diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 6eea39119e..26a595d14d 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -51,11 +51,12 @@ jobs: command: fmt args: --manifest-path ./bindings/wasm/Cargo.toml --all -- --check - - name: stronghold-nodejs fmt check - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --manifest-path ./bindings/stronghold-nodejs/Cargo.toml --all -- --check + # Deactivated while the crate is broken, which is the case due to the removal of identity_account_storage. + # - name: stronghold-nodejs fmt check + # uses: actions-rs/cargo@v1 + # with: + # command: fmt + # args: --manifest-path ./bindings/stronghold-nodejs/Cargo.toml --all -- --check - name: libjose fmt check uses: actions-rs/cargo@v1 diff --git a/Cargo.toml b/Cargo.toml index 2969633cda..5ee3b519d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" members = [ + "identity_agent", # "identity_comm", "identity_core", "identity_credential", @@ -13,10 +14,6 @@ members = [ ] exclude = [ - "identity_account", - "identity_account_storage", - "identity_agent", - "examples_legacy", "identity_iota_core_legacy", "bindings/stronghold-nodejs", "bindings/wasm", diff --git a/bindings/stronghold-nodejs/Cargo.lock b/bindings/stronghold-nodejs/Cargo.lock index 7e156bd960..c46a1657a3 100644 --- a/bindings/stronghold-nodejs/Cargo.lock +++ b/bindings/stronghold-nodejs/Cargo.lock @@ -2,52 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures 0.2.2", - "opaque-debug", -] - -[[package]] -name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.6", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "0.7.18" @@ -57,29 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "anyhow" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" - -[[package]] -name = "async-trait" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atom" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcfa7026c8fa43fa767bff0b7cd4d1963cdfd946e0386bee93003e9b2498562" - [[package]] name = "atty" version = "0.2.14" @@ -163,28 +94,13 @@ dependencies = [ "serde", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "blake2" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" dependencies = [ - "crypto-mac 0.8.0", + "crypto-mac", "digest 0.9.0", "opaque-debug", ] @@ -234,52 +150,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chacha20" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee7ad89dc1128635074c268ee661f90c3f7e83d9fd12910608c36b47d6c3412" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures 0.1.5", - "zeroize", -] - -[[package]] -name = "chacha20poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580317203210c517b6d44794abfbe600698276db18127e37ad3e69bf5e848e5" -dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", - "zeroize", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - [[package]] name = "colored" version = "1.9.3" @@ -297,15 +173,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" -[[package]] -name = "cpufeatures" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" -dependencies = [ - "libc", -] - [[package]] name = "cpufeatures" version = "0.2.2" @@ -335,16 +202,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "ctor" version = "0.1.22" @@ -355,15 +212,6 @@ dependencies = [ "syn", ] -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -433,47 +281,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "ed25519-zebra" version = "2.2.0" @@ -521,95 +328,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" - -[[package]] -name = "futures-executor" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-macro" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" - -[[package]] -name = "futures-task" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" - -[[package]] -name = "futures-util" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - [[package]] name = "generic-array" version = "0.14.5" @@ -644,25 +362,11 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ghash" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", - "serde", -] [[package]] name = "heck" @@ -685,34 +389,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.3", -] - [[package]] name = "identity-diff" version = "0.7.0-alpha.3" @@ -727,7 +403,6 @@ dependencies = [ name = "identity-stronghold-nodejs" version = "0.7.0-alpha.3" dependencies = [ - "identity_account_storage", "identity_core", "identity_did", "identity_iota_core_legacy", @@ -738,28 +413,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "identity_account_storage" -version = "0.7.0-alpha.1" -dependencies = [ - "async-trait", - "futures", - "hashbrown", - "identity_core", - "identity_did", - "identity_iota_core_legacy", - "iota-crypto 0.12.1", - "iota_stronghold", - "once_cell", - "rand", - "seahash", - "serde", - "strum", - "thiserror", - "tokio", - "zeroize", -] - [[package]] name = "identity_core" version = "0.7.0-alpha.3" @@ -827,24 +480,6 @@ dependencies = [ "serde", ] -[[package]] -name = "iota-crypto" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee273ae67ff1bc7c59317c0ab280e0e76259e6bd83e140ac4c2ecebec994f740" -dependencies = [ - "aead", - "blake2 0.9.2", - "chacha20poly1305", - "curve25519-dalek", - "digest 0.9.0", - "generic-array", - "getrandom 0.2.6", - "hmac 0.11.0", - "sha2 0.9.9", - "x25519-dalek", -] - [[package]] name = "iota-crypto" version = "0.9.1" @@ -864,41 +499,13 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f03717e934972fad6f1c9b4cd25f662e1753b58a7f76e3dceadeb646e034b252" dependencies = [ - "aead", - "aes", - "aes-gcm", "blake2 0.10.4", - "chacha20poly1305", "curve25519-dalek", "digest 0.10.3", "ed25519-zebra 3.0.0", - "generic-array", "getrandom 0.2.6", - "hmac 0.12.1", - "pbkdf2", - "serde", "sha2 0.10.2", - "unicode-normalization", "x25519-dalek", - "zeroize", -] - -[[package]] -name = "iota_stronghold" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ebdd443fde5c02437469c1a318aa4d56cba83db3913743862a0e3b54ab5736" -dependencies = [ - "bincode", - "hkdf", - "iota-crypto 0.12.1", - "serde", - "stronghold-derive", - "stronghold-rlu", - "stronghold-utils", - "stronghold_engine", - "thiserror", - "zeroize", ] [[package]] @@ -941,18 +548,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "libsodium-sys" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd" -dependencies = [ - "cc", - "libc", - "pkg-config", - "walkdir", -] - [[package]] name = "log" version = "0.4.17" @@ -1083,21 +678,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "paste" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.3", -] - [[package]] name = "percent-encoding" version = "2.1.0" @@ -1110,47 +690,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - -[[package]] -name = "poly1305" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" -dependencies = [ - "cpufeatures 0.2.2", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.2", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.39" @@ -1169,27 +708,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.3", -] - [[package]] name = "rand_core" version = "0.5.1" @@ -1204,29 +722,6 @@ name = "rand_core" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.6", -] - -[[package]] -name = "redox_syscall" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom 0.2.6", - "redox_syscall", - "thiserror", -] [[package]] name = "regex" @@ -1263,21 +758,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - [[package]] name = "serde" version = "1.0.137" @@ -1328,7 +808,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if", - "cpufeatures 0.2.2", + "cpufeatures", "digest 0.9.0", "opaque-debug", ] @@ -1340,89 +820,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ "cfg-if", - "cpufeatures 0.2.2", + "cpufeatures", "digest 0.10.3", ] -[[package]] -name = "slab" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" - [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "stronghold-derive" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac933fa00ab0d0f1d6327ab9198a95aaaca3458dae09f0b8e1cd69fee2a7eaf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "stronghold-rlu" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1441b94679d035a7f883515e283c0166bab3d87079bbe0cc76f11699c4c336d9" -dependencies = [ - "atom", - "lazy_static", - "log", - "thiserror", - "zeroize", -] - -[[package]] -name = "stronghold-runtime" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8845d2c5a4270ecdf542f489ff16b2a6fec245ad001c6afe098c2e6ea0211d6c" -dependencies = [ - "dirs", - "iota-crypto 0.8.0", - "libsodium-sys", - "rand", - "serde", - "thiserror", - "zeroize", -] - -[[package]] -name = "stronghold-utils" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1ce0d6205e8ea89771f2b32d64aeaecbc930320919e95ec4efcc0aa96cd951" -dependencies = [ - "rand", - "stronghold-derive", -] - -[[package]] -name = "stronghold_engine" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4131515d37c48994295d0dd7f99bb178332fe8c4cf0ab361a7ee7d4d62c2da" -dependencies = [ - "anyhow", - "dirs-next", - "hex", - "iota-crypto 0.8.0", - "once_cell", - "paste", - "serde", - "stronghold-runtime", - "thiserror", - "zeroize", -] - [[package]] name = "strum" version = "0.24.0" @@ -1574,16 +981,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "url" version = "2.2.2" @@ -1603,17 +1000,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1696,15 +1082,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/bindings/stronghold-nodejs/Cargo.toml b/bindings/stronghold-nodejs/Cargo.toml index a7ea43c625..63ae2991ef 100644 --- a/bindings/stronghold-nodejs/Cargo.toml +++ b/bindings/stronghold-nodejs/Cargo.toml @@ -7,7 +7,7 @@ publish = false crate-type = ["cdylib"] [dependencies] -identity_account_storage = { version = "0.7.0-alpha.1", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } +# identity_account_storage = { version = "0.7.0-alpha.1", path = "../../identity_account_storage", default-features = false, features = ["stronghold", "send-sync-storage", "encryption"] } identity_core = { version = "0.7.0-alpha.3", path = "../../identity_core", default-features = false } identity_did = { version = "0.7.0-alpha.3", path = "../../identity_did", default-features = false } identity_iota_core_legacy = { version = "0.7.0-alpha.1", path = "../../identity_iota_core_legacy", default-features = false } diff --git a/bindings/wasm/src/account/identity/chain_state.rs b/bindings/wasm/src/account/identity/chain_state.rs deleted file mode 100644 index 789bd46642..0000000000 --- a/bindings/wasm/src/account/identity/chain_state.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account_storage::ChainState; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen(js_name = ChainState, inspectable)] -pub struct WasmChainState(pub(crate) ChainState); - -impl_wasm_json!(WasmChainState, ChainState); -impl_wasm_clone!(WasmChainState, ChainState); - -impl From for WasmChainState { - fn from(chain_state: ChainState) -> Self { - WasmChainState(chain_state) - } -} - -impl From for ChainState { - fn from(wasm_chain_state: WasmChainState) -> Self { - wasm_chain_state.0 - } -} diff --git a/bindings/wasm/src/account/identity/mod.rs b/bindings/wasm/src/account/identity/mod.rs deleted file mode 100644 index d375f63cdb..0000000000 --- a/bindings/wasm/src/account/identity/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use chain_state::WasmChainState; - -mod chain_state; diff --git a/bindings/wasm/src/account/storage/mod.rs b/bindings/wasm/src/account/storage/mod.rs deleted file mode 100644 index 94486c31f9..0000000000 --- a/bindings/wasm/src/account/storage/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use traits::WasmStorage; - -mod test_suite; -mod traits; diff --git a/bindings/wasm/src/account/storage/test_suite.rs b/bindings/wasm/src/account/storage/test_suite.rs deleted file mode 100644 index aa6660da53..0000000000 --- a/bindings/wasm/src/account/storage/test_suite.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account_storage::StorageTestSuite; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::common::PromiseVoid; - -use super::WasmStorage; - -/// A test suite for the `Storage` interface. -/// -/// This module contains a set of tests that a correct storage implementation -/// should pass. Note that not every edge case is tested. -/// -/// Tests usually rely on multiple interface methods being implemented, so they should only -/// be run on a fully implemented version. That's why there is not a single test case for every -/// interface method. -#[wasm_bindgen(js_name = StorageTestSuite)] -pub struct WasmStorageTestSuite; - -macro_rules! expose_to_wasm { - ($test_name:ident, $js_name:ident) => { - #[wasm_bindgen(js_class = StorageTestSuite)] - impl WasmStorageTestSuite { - #[wasm_bindgen(js_name = $js_name)] - pub fn $test_name(storage: WasmStorage) -> PromiseVoid { - let promise = future_to_promise(async move { - StorageTestSuite::$test_name(storage) - .await - .map_err(|err| { - let errors: Vec = err.chain().map(|error| error.to_string()).collect(); - let output: String = AsRef::<[String]>::as_ref(&errors).join(": "); - JsValue::from_str(&output) - }) - .map(|_| JsValue::undefined()) - }); - - promise.unchecked_into::() - } - } - }; -} - -expose_to_wasm!(did_create_generate_key_test, didCreateGenerateKeyTest); -expose_to_wasm!(did_create_private_key_test, didCreatePrivateKeyTest); -expose_to_wasm!(did_list_test, didListTest); -expose_to_wasm!(did_purge_test, didPurgeTest); -expose_to_wasm!(key_generate_test, keyGenerateTest); -expose_to_wasm!(key_delete_test, keyDeleteTest); -expose_to_wasm!(key_insert_test, keyInsertTest); -expose_to_wasm!(key_sign_ed25519_test, keySignEd25519Test); - -#[wasm_bindgen(js_class = StorageTestSuite)] -impl WasmStorageTestSuite { - #[wasm_bindgen(js_name = encryptionTest)] - pub fn encryption_test(alice_storage: WasmStorage, bob_storage: WasmStorage) -> PromiseVoid { - let promise = future_to_promise(async move { - StorageTestSuite::encryption_test(alice_storage, bob_storage) - .await - .map_err(|err| { - let errors: Vec = err.chain().map(|error| error.to_string()).collect(); - let output: String = AsRef::<[String]>::as_ref(&errors).join(": "); - JsValue::from_str(&output) - }) - .map(|_| JsValue::undefined()) - }); - - promise.unchecked_into::() - } -} diff --git a/bindings/wasm/src/account/storage/traits.rs b/bindings/wasm/src/account/storage/traits.rs deleted file mode 100644 index 5a6d549007..0000000000 --- a/bindings/wasm/src/account/storage/traits.rs +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Debug; -use core::fmt::Formatter; - -use identity_iota::account_storage::CekAlgorithm; -use identity_iota::account_storage::DIDType; -use identity_iota::account_storage::EncryptedData; -use identity_iota::account_storage::EncryptionAlgorithm; -use identity_iota::account_storage::Error as AccountStorageError; -use identity_iota::account_storage::KeyLocation; -use identity_iota::account_storage::Result as AccountStorageResult; -use identity_iota::account_storage::Signature; -use identity_iota::account_storage::Storage; -use identity_iota::crypto::PrivateKey; -use identity_iota::crypto::PublicKey; -use identity_iota::did::CoreDID; -use identity_iota::iota::NetworkName; -use identity_iota::prelude::KeyType; -use js_sys::Array; -use js_sys::Promise; -use js_sys::Uint8Array; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::JsFuture; - -use crate::account::types::WasmCekAlgorithm; -use crate::account::types::WasmDIDType; -use crate::account::types::WasmEncryptedData; -use crate::account::types::WasmEncryptionAlgorithm; -use crate::account::types::WasmKeyLocation; -use crate::common::PromiseBool; -use crate::common::PromiseVoid; -use crate::crypto::WasmKeyType; -use crate::did::WasmCoreDID; -use crate::error::JsValueResult; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromisePublicKey; - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseSignature; - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseKeyLocation; - #[wasm_bindgen(typescript_type = "Promise>")] - pub type PromiseArrayDID; - #[wasm_bindgen(typescript_type = "Promise<[CoreDID, KeyLocation]>")] - pub type PromiseDIDKeyLocation; - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseEncryptedData; - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseData; - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseOptionBytes; -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Storage")] - pub type WasmStorage; - - #[wasm_bindgen(method, js_name = didCreate)] - pub fn did_create( - this: &WasmStorage, - did_type: WasmDIDType, - network: &str, - fragment: &str, - private_key: Option>, - ) -> PromiseDIDKeyLocation; - #[wasm_bindgen(method, js_name = didPurge)] - pub fn did_purge(this: &WasmStorage, did: WasmCoreDID) -> PromiseVoid; - - #[wasm_bindgen(method, js_name = didExists)] - pub fn did_exists(this: &WasmStorage, did: WasmCoreDID) -> PromiseBool; - #[wasm_bindgen(method, js_name = didList)] - pub fn did_list(this: &WasmStorage) -> PromiseArrayDID; - - #[wasm_bindgen(method, js_name = keyGenerate)] - pub fn key_generate( - this: &WasmStorage, - did: WasmCoreDID, - key_type: WasmKeyType, - fragment: String, - ) -> PromiseKeyLocation; - #[wasm_bindgen(method, js_name = keyInsert)] - pub fn key_insert( - this: &WasmStorage, - did: WasmCoreDID, - location: WasmKeyLocation, - private_key: Vec, - ) -> PromiseVoid; - #[wasm_bindgen(method, js_name = keyPublic)] - pub fn key_public(this: &WasmStorage, did: WasmCoreDID, location: WasmKeyLocation) -> PromisePublicKey; - #[wasm_bindgen(method, js_name = keyDelete)] - pub fn key_delete(this: &WasmStorage, did: WasmCoreDID, location: WasmKeyLocation) -> PromiseVoid; - #[wasm_bindgen(method, js_name = keySign)] - pub fn key_sign(this: &WasmStorage, did: WasmCoreDID, location: WasmKeyLocation, data: Vec) -> PromiseSignature; - #[wasm_bindgen(method, js_name = keyExists)] - pub fn key_exists(this: &WasmStorage, did: WasmCoreDID, location: WasmKeyLocation) -> PromiseBool; - #[wasm_bindgen(method, js_name = dataEncrypt)] - pub fn data_encrypt( - this: &WasmStorage, - did: WasmCoreDID, - plaintext: Vec, - associated_data: Vec, - encryption_algorithm: WasmEncryptionAlgorithm, - cek_algorithm: WasmCekAlgorithm, - public_key: Vec, - ) -> PromiseEncryptedData; - #[wasm_bindgen(method, js_name = dataDecrypt)] - pub fn data_decrypt( - this: &WasmStorage, - did: WasmCoreDID, - data: WasmEncryptedData, - encryption_algorithm: WasmEncryptionAlgorithm, - cek_algorithm: WasmCekAlgorithm, - private_key: WasmKeyLocation, - ) -> Uint8Array; - #[wasm_bindgen(method, js_name = blobGet)] - pub fn blob_get(this: &WasmStorage, did: WasmCoreDID) -> PromiseOptionBytes; - #[wasm_bindgen(method, js_name = blobSet)] - pub fn blob_set(this: &WasmStorage, did: WasmCoreDID, blob: Vec) -> PromiseVoid; - #[wasm_bindgen(method, js_name = flushChanges)] - pub fn flush_changes(this: &WasmStorage) -> PromiseVoid; -} - -impl Debug for WasmStorage { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.write_str("WasmStorage") - } -} - -#[async_trait::async_trait(?Send)] -impl Storage for WasmStorage { - async fn did_create( - &self, - did_type: DIDType, - network: NetworkName, - fragment: &str, - private_key: Option, - ) -> AccountStorageResult<(CoreDID, KeyLocation)> { - let private_key: Option> = private_key.map(|key| { - let key_bytes: Vec = key.as_ref().to_vec(); - core::mem::drop(key); - key_bytes - }); - - let promise: Promise = Promise::resolve(&self.did_create(did_type.into(), network.as_ref(), fragment, private_key)); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let did_location_tuple: js_sys::Array = js_sys::Array::from(&result.to_account_error()?); - let mut did_location_tuple: js_sys::ArrayIter = did_location_tuple.iter(); - - let did: CoreDID = did_location_tuple - .next() - .ok_or_else(|| AccountStorageError::JsError("expected a tuple of size 2".to_owned()))? - .into_serde() - .map_err(|err| AccountStorageError::SerializationError(err.to_string()))?; - - let location: KeyLocation = did_location_tuple - .next() - .ok_or_else(|| AccountStorageError::JsError("expected a tuple of size 2".to_owned()))? - .into_serde() - .map_err(|err| AccountStorageError::SerializationError(err.to_string()))?; - - Ok((did, location)) - } - - async fn did_purge(&self, did: &CoreDID) -> AccountStorageResult { - let promise: Promise = Promise::resolve(&self.did_purge(did.clone().into())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - result.into() - } - - async fn did_exists(&self, did: &CoreDID) -> AccountStorageResult { - let promise: Promise = Promise::resolve(&self.did_exists(did.clone().into())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - result.into() - } - - async fn did_list(&self) -> AccountStorageResult> { - let promise: Promise = Promise::resolve(&self.did_list()); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let js_value: JsValue = result.to_account_error()?; - - js_value - .into_serde() - .map_err(|err| AccountStorageError::SerializationError(err.to_string())) - } - - async fn key_generate(&self, did: &CoreDID, key_type: KeyType, fragment: &str) -> AccountStorageResult { - let promise: Promise = - Promise::resolve(&self.key_generate(did.clone().into(), key_type.into(), fragment.to_owned())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let location: KeyLocation = result - .to_account_error()? - .into_serde() - .map_err(|err| AccountStorageError::SerializationError(err.to_string()))?; - - Ok(location) - } - - async fn key_insert( - &self, - did: &CoreDID, - location: &KeyLocation, - private_key: PrivateKey, - ) -> AccountStorageResult<()> { - let promise: Promise = Promise::resolve(&self.key_insert( - did.clone().into(), - location.clone().into(), - private_key.as_ref().to_vec(), - )); - let result: JsValueResult = JsFuture::from(promise).await.into(); - result.into() - } - - async fn key_public(&self, did: &CoreDID, location: &KeyLocation) -> AccountStorageResult { - let promise: Promise = Promise::resolve(&self.key_public(did.clone().into(), location.clone().into())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let public_key: Vec = result.to_account_error().map(uint8array_to_bytes)??; - Ok(public_key.into()) - } - - async fn key_delete(&self, did: &CoreDID, location: &KeyLocation) -> AccountStorageResult { - let promise: Promise = Promise::resolve(&self.key_delete(did.clone().into(), location.clone().into())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - result.into() - } - - async fn key_sign(&self, did: &CoreDID, location: &KeyLocation, data: Vec) -> AccountStorageResult { - let promise: Promise = Promise::resolve(&self.key_sign(did.clone().into(), location.clone().into(), data)); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let js_value: JsValue = result.to_account_error()?; - let signature: Signature = js_value - .into_serde() - .map_err(|err| AccountStorageError::SerializationError(err.to_string()))?; - Ok(signature) - } - - async fn key_exists(&self, did: &CoreDID, location: &KeyLocation) -> AccountStorageResult { - let promise: Promise = Promise::resolve(&self.key_exists(did.clone().into(), location.clone().into())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - result.into() - } - - async fn data_encrypt( - &self, - did: &CoreDID, - plaintext: Vec, - associated_data: Vec, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - public_key: PublicKey, - ) -> AccountStorageResult { - let promise: Promise = Promise::resolve(&self.data_encrypt( - did.clone().into(), - plaintext, - associated_data, - (*encryption_algorithm).into(), - cek_algorithm.clone().into(), - public_key.as_ref().to_vec(), - )); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let encrypted_data: EncryptedData = result - .to_account_error()? - .into_serde() - .map_err(|err| AccountStorageError::SerializationError(err.to_string()))?; - Ok(encrypted_data) - } - - async fn data_decrypt( - &self, - did: &CoreDID, - data: EncryptedData, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - private_key: &KeyLocation, - ) -> AccountStorageResult> { - let promise: Promise = Promise::resolve(&self.data_decrypt( - did.clone().into(), - data.into(), - (*encryption_algorithm).into(), - cek_algorithm.clone().into(), - private_key.clone().into(), - )); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let data: Vec = result.to_account_error().map(uint8array_to_bytes)??; - Ok(data) - } - - async fn blob_get(&self, did: &CoreDID) -> AccountStorageResult>> { - let promise: Promise = Promise::resolve(&self.blob_get(did.clone().into())); - let result: JsValueResult = JsFuture::from(promise).await.into(); - let js_value: JsValue = result.to_account_error()?; - if js_value.is_null() || js_value.is_undefined() { - return Ok(None); - } - let value: Vec = uint8array_to_bytes(js_value)?; - Ok(Some(value)) - } - - async fn blob_set(&self, did: &CoreDID, blob: Vec) -> AccountStorageResult<()> { - let promise: Promise = Promise::resolve(&self.blob_set(did.clone().into(), blob)); - let result: JsValueResult = JsFuture::from(promise).await.into(); - result.into() - } - - async fn flush_changes(&self) -> AccountStorageResult<()> { - let promise: Promise = Promise::resolve(&self.flush_changes()); - let result: JsValueResult = JsFuture::from(promise).await.into(); - result.into() - } -} - -#[wasm_bindgen(typescript_custom_section)] -const STORAGE: &'static str = r#" -/** An interface for Account storage implementations. - -The `Storage` interface is used for secure key operations, such as key generation and signing, -as well as key-value like storage of data structures, such as DID documents. - -# Identifiers - -Implementations of this interface are expected to uniquely identify keys through the -combination of DID _and_ `KeyLocation`. - -An implementation recommendation is to use the DID as a partition key. Everything related to a DID -can be stored in a partition identified by that DID. Keys belonging to a DID can then be identified -by `KeyLocation`s in that partition. - -# DID List - -The storage is expected to maintain a list of stored DIDs. DIDs created with `did_create` should be -inserted into the list, and removed when calling `did_purge`. -Other operations on the list are `did_exists` and `did_list`. - -# Implementation example - -See the `MemStore` example for a test implementation. */ -interface Storage { - /** Creates a new identity of the type declared in `did_type` for the given `network`. - - - Uses the given Ed25519 `private_key` or generates a new key if it's `None`. - - Returns an error if the DID already exists. - - Adds the newly created DID to a list which can be accessed via `did_list`. - - Returns the generated DID represented as a [`CoreDID`] and the location at which the key was stored. */ - didCreate: (didType: DIDType, network: string, fragment: string, privateKey?: Uint8Array) => Promise<[CoreDID, KeyLocation]>; - - /** Removes the keys and any other state for the given `did`. - - This operation is idempotent: it does not fail if the given `did` does not (or no longer) exist. - - Returns `true` if the did and its associated data was removed, `false` if nothing was done. */ - didPurge: (did: CoreDID) => Promise; - - /** Returns `true` if `did` exists in the list of stored DIDs. */ - didExists: (did: CoreDID) => Promise; - - /** Returns the list of stored DIDs. */ - didList: () => Promise>; - - /** Generates a new key for the given `did` with the given `key_type` and `fragment` identifier - and returns the location of the newly generated key. */ - keyGenerate: (did: CoreDID, keyType: KeyType, fragment: string) => Promise; - - /** Inserts a private key at the specified `location`. - - If a key at `location` exists, it is overwritten. */ - keyInsert: (did: CoreDID, keyLocation: KeyLocation, privateKey: Uint8Array) => Promise; - - /** Retrieves the public key from `location`. */ - keyPublic: (did: CoreDID, keyLocation: KeyLocation) => Promise; - - /** Deletes the key at `location`. - - This operation is idempotent: it does not fail if the key does not exist. - - Returns `true` if it removed the key, `false` if nothing was done. */ - keyDelete: (did: CoreDID, keyLocation: KeyLocation) => Promise; - - /** Signs `data` with the private key at the specified `location`. */ - keySign: (did: CoreDID, keyLocation: KeyLocation, data: Uint8Array) => Promise; - - /** Returns `true` if a key exists at the specified `location`. */ - keyExists: (did: CoreDID, keyLocation: KeyLocation) => Promise; - - /** Encrypts the given `plaintext` with the specified `encryptionAlgorithm` and `cekAlgorithm`. - * - * Returns an `EncryptedData` instance. - */ - dataEncrypt: (did: CoreDID, plaintext: Uint8Array, associatedData: Uint8Array, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, publicKey: Uint8Array) => Promise; - - /** Decrypts the given `data` with the specified `encryptionAlgorithm` and `cekAlgorithm`. - * - * Returns the decrypted text. - */ - dataDecrypt: (did: CoreDID, data: EncryptedData, encryptionAlgorithm: EncryptionAlgorithm, cekAlgorithm: CekAlgorithm, privateKey: KeyLocation) => Promise; - - /** Returns the blob stored by the identity specified by `did`. */ - blobGet: (did: CoreDID) => Promise; - - /** Stores an arbitrary blob for the identity specified by `did`. */ - blobSet: (did: CoreDID, blob: Uint8Array) => Promise; - - /** Persists any unsaved changes. */ - flushChanges: () => Promise; -}"#; - -fn uint8array_to_bytes(value: JsValue) -> AccountStorageResult> { - if !JsCast::is_instance_of::(&value) { - return Err(AccountStorageError::SerializationError( - "expected Uint8Array".to_owned(), - )); - } - let array_js_value = JsValue::from(Array::from(&value)); - array_js_value - .into_serde() - .map_err(|e| AccountStorageError::SerializationError(e.to_string())) -} diff --git a/bindings/wasm/src/account/types/auto_save.rs b/bindings/wasm/src/account/types/auto_save.rs deleted file mode 100644 index 53ad86904b..0000000000 --- a/bindings/wasm/src/account/types/auto_save.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account::AutoSave; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "AutoSave | undefined")] - pub type OptionAutoSave; -} - -#[wasm_bindgen(js_name = AutoSave, inspectable)] -pub struct WasmAutoSave(pub(crate) AutoSave); - -/// Available auto-save behaviours. -#[wasm_bindgen(js_class = AutoSave)] -impl WasmAutoSave { - /// Never save. - #[wasm_bindgen] - pub fn never() -> WasmAutoSave { - Self(AutoSave::Never) - } - - /// Save after every action. - #[wasm_bindgen] - pub fn every() -> WasmAutoSave { - Self(AutoSave::Every) - } - - /// Save after every N actions. - #[wasm_bindgen] - pub fn batch(number_of_actions: usize) -> WasmAutoSave { - Self(AutoSave::Batch(number_of_actions)) - } -} - -impl_wasm_json!(WasmAutoSave, AutoSave); diff --git a/bindings/wasm/src/account/types/did_type.rs b/bindings/wasm/src/account/types/did_type.rs deleted file mode 100644 index 9fff9df331..0000000000 --- a/bindings/wasm/src/account/types/did_type.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account_storage::DIDType; -use wasm_bindgen::prelude::*; - -/// Supported types representing a DID that can be generated by the storage interface. -#[wasm_bindgen(js_name = DIDType)] -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -pub enum WasmDIDType { - IotaDID, -} - -impl From for DIDType { - fn from(other: WasmDIDType) -> Self { - match other { - WasmDIDType::IotaDID => DIDType::IotaDID, - } - } -} - -impl From for WasmDIDType { - fn from(other: DIDType) -> Self { - match other { - DIDType::IotaDID => WasmDIDType::IotaDID, - } - } -} diff --git a/bindings/wasm/src/account/types/identity_setup.rs b/bindings/wasm/src/account/types/identity_setup.rs deleted file mode 100644 index 300a70b166..0000000000 --- a/bindings/wasm/src/account/types/identity_setup.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account::IdentitySetup; -use identity_iota::crypto::PrivateKey; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "IdentitySetup")] - pub type WasmIdentitySetup; - - #[wasm_bindgen(getter, method)] - pub fn privateKey(this: &WasmIdentitySetup) -> Option>; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_IDENTITY_SETUP: &'static str = r#" -/** - * Configuration used to create a new Identity. - */ -export type IdentitySetup = { - /** - * Use a pre-generated Ed25519 private key for the DID. - */ - privateKey?: Uint8Array, -}; -"#; - -impl From for IdentitySetup { - fn from(wasm_setup: WasmIdentitySetup) -> Self { - let mut setup: IdentitySetup = IdentitySetup::new(); - if let Some(private_key) = wasm_setup.privateKey() { - setup = setup.private_key(PrivateKey::from(private_key)); - } - setup - } -} diff --git a/bindings/wasm/src/account/types/key_location.rs b/bindings/wasm/src/account/types/key_location.rs deleted file mode 100644 index 40a14f8b34..0000000000 --- a/bindings/wasm/src/account/types/key_location.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account_storage::KeyLocation; -use wasm_bindgen::prelude::*; - -use crate::crypto::WasmKeyType; -use crate::did::WasmVerificationMethod; -use crate::error::Result; -use crate::error::WasmResult; - -/// The storage location of a verification method key. -/// -/// A key is uniquely identified by the fragment and a hash of its public key. -/// Importantly, the fragment alone is insufficient to represent the storage location. -/// For example, when rotating a key, there will be two keys in storage for the -/// same identity with the same fragment. The `key_hash` disambiguates the keys in -/// situations like these. -/// -/// The string representation of that location can be obtained via `canonicalRepr`. -#[wasm_bindgen(js_name = KeyLocation, inspectable)] -pub struct WasmKeyLocation(pub(crate) KeyLocation); - -#[wasm_bindgen(js_class = KeyLocation)] -impl WasmKeyLocation { - /// Create a location from a `KeyType`, the fragment of a verification method - /// and the bytes of a public key. - #[wasm_bindgen(constructor)] - #[allow(non_snake_case)] - pub fn new(keyType: WasmKeyType, fragment: String, publicKey: Vec) -> WasmKeyLocation { - WasmKeyLocation(KeyLocation::new(keyType.into(), fragment, publicKey.as_ref())) - } - - /// Obtain the location of a verification method's key in storage. - #[wasm_bindgen(js_name = fromVerificationMethod)] - pub fn from_verification_method(method: &WasmVerificationMethod) -> Result { - KeyLocation::from_verification_method(&method.0) - .map(WasmKeyLocation) - .wasm_result() - } - - /// Returns the canonical string representation of the location. - /// - /// This should be used as the representation for storage keys. - #[wasm_bindgen] - pub fn canonical(&self) -> String { - self.0.canonical() - } - - /// Returns a copy of the key type of the key location. - #[wasm_bindgen(js_name = keyType)] - pub fn key_type(&self) -> WasmKeyType { - self.0.key_type.into() - } - - #[wasm_bindgen(js_name = toString)] - #[allow(clippy::inherent_to_string)] - pub fn to_string(&self) -> String { - self.0.to_string() - } -} - -impl_wasm_json!(WasmKeyLocation, KeyLocation); - -impl From for KeyLocation { - fn from(wasm_key_location: WasmKeyLocation) -> Self { - wasm_key_location.0 - } -} - -impl From for WasmKeyLocation { - fn from(wasm_key_location: KeyLocation) -> Self { - WasmKeyLocation(wasm_key_location) - } -} diff --git a/bindings/wasm/src/account/types/method_content.rs b/bindings/wasm/src/account/types/method_content.rs deleted file mode 100644 index 5b1781af55..0000000000 --- a/bindings/wasm/src/account/types/method_content.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account::MethodContent; -use identity_iota::crypto::PrivateKey; -use identity_iota::crypto::PublicKey; -use serde::Deserialize; -use serde::Serialize; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "MethodContent | undefined")] - pub type OptionMethodContent; -} - -// Workaround for having to deserialize `WasmMethodContent` from a Typescript interface -// TODO: remove when https://github.com/rustwasm/wasm-bindgen/pull/2677 is merged. -#[derive(Serialize, Deserialize)] -enum WasmMethodContentInner { - GenerateEd25519, - PrivateEd25519(Vec), - PublicEd25519(Vec), - GenerateX25519, - PrivateX25519(Vec), - PublicX25519(Vec), -} - -#[wasm_bindgen(js_name = MethodContent, inspectable)] -#[derive(Serialize, Deserialize)] -pub struct WasmMethodContent(WasmMethodContentInner); - -#[wasm_bindgen(js_class = MethodContent)] -impl WasmMethodContent { - /// Generate and store a new Ed25519 keypair for a new `Ed25519VerificationKey2018` method. - #[wasm_bindgen(js_name = GenerateEd25519)] - pub fn generate_ed25519() -> WasmMethodContent { - Self(WasmMethodContentInner::GenerateEd25519) - } - - /// Store an existing Ed25519 private key and derive a public key from it for a new - /// `Ed25519VerificationKey2018` method. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = PrivateEd25519)] - pub fn private_ed25519(privateKey: Vec) -> WasmMethodContent { - Self(WasmMethodContentInner::PrivateEd25519(privateKey)) - } - - /// Insert an existing Ed25519 public key into a new `Ed25519VerificationKey2018` method, - /// without generating or storing a private key. - /// - /// NOTE: the method will be unable to be used to sign anything without a private key. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = PublicEd25519)] - pub fn public_ed25519(publicKey: Vec) -> WasmMethodContent { - Self(WasmMethodContentInner::PublicEd25519(publicKey)) - } - - /// Generate and store a new X25519 keypair for a new `X25519KeyAgreementKey2019` method. - #[wasm_bindgen(js_name = GenerateX25519)] - pub fn generate_x25519() -> WasmMethodContent { - Self(WasmMethodContentInner::GenerateX25519) - } - - /// Store an existing X25519 private key and derive a public key from it for a new - /// `X25519KeyAgreementKey2019` method. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = PrivateX25519)] - pub fn private_x25519(privateKey: Vec) -> WasmMethodContent { - Self(WasmMethodContentInner::PrivateX25519(privateKey)) - } - - /// Insert an existing X25519 public key into a new `X25519KeyAgreementKey2019` method, - /// without generating or storing a private key. - /// - /// NOTE: the method will be unable to be used for key exchange without a private key. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = PublicX25519)] - pub fn public_x25519(publicKey: Vec) -> WasmMethodContent { - Self(WasmMethodContentInner::PublicX25519(publicKey)) - } -} - -impl_wasm_json!(WasmMethodContent, MethodContent); - -impl From for MethodContent { - fn from(content: WasmMethodContent) -> Self { - match content.0 { - WasmMethodContentInner::GenerateEd25519 => MethodContent::GenerateEd25519, - WasmMethodContentInner::PrivateEd25519(private_key) => { - MethodContent::PrivateEd25519(PrivateKey::from(private_key)) - } - WasmMethodContentInner::PublicEd25519(public_key) => MethodContent::PublicEd25519(PublicKey::from(public_key)), - WasmMethodContentInner::GenerateX25519 => MethodContent::GenerateX25519, - WasmMethodContentInner::PrivateX25519(private_key) => MethodContent::PrivateX25519(PrivateKey::from(private_key)), - WasmMethodContentInner::PublicX25519(public_key) => MethodContent::PublicX25519(PublicKey::from(public_key)), - } - } -} diff --git a/bindings/wasm/src/account/types/mod.rs b/bindings/wasm/src/account/types/mod.rs deleted file mode 100644 index a43c7c50b9..0000000000 --- a/bindings/wasm/src/account/types/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use agreement_info::WasmAgreementInfo; -pub use auto_save::OptionAutoSave; -pub use auto_save::WasmAutoSave; -pub use cek_algorithm::WasmCekAlgorithm; -pub use did_type::WasmDIDType; -pub use encrypted_data::WasmEncryptedData; -pub use encryption_algorithm::WasmEncryptionAlgorithm; -pub use identity_setup::WasmIdentitySetup; -pub use key_location::WasmKeyLocation; -pub use method_content::*; -pub use signature::WasmSignature; - -mod agreement_info; -mod auto_save; -mod cek_algorithm; -mod did_type; -mod encrypted_data; -mod encryption_algorithm; -mod identity_setup; -mod key_location; -mod method_content; -mod signature; diff --git a/bindings/wasm/src/account/types/signature.rs b/bindings/wasm/src/account/types/signature.rs deleted file mode 100644 index cf03389b70..0000000000 --- a/bindings/wasm/src/account/types/signature.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account_storage::Signature; -use wasm_bindgen::prelude::*; - -/// A digital signature. -#[wasm_bindgen(js_name = Signature, inspectable)] -pub struct WasmSignature(pub(crate) Signature); - -#[wasm_bindgen(js_class = Signature)] -impl WasmSignature { - /// Creates a new `Signature`. - #[wasm_bindgen(constructor)] - pub fn new(data: Vec) -> WasmSignature { - WasmSignature(Signature::new(data)) - } - - /// Returns a copy of the signature as a `UInt8Array`. - #[wasm_bindgen(js_name = asBytes)] - pub fn as_bytes(&self) -> Vec { - self.0.as_bytes().to_vec() - } -} - -impl_wasm_json!(WasmSignature, Signature); - -impl From for Signature { - fn from(wasm_signature: WasmSignature) -> Self { - wasm_signature.0 - } -} diff --git a/bindings/wasm/src/account/wasm_account/account.rs b/bindings/wasm/src/account/wasm_account/account.rs deleted file mode 100644 index 322cc8e49e..0000000000 --- a/bindings/wasm/src/account/wasm_account/account.rs +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cell::RefCell; -use std::rc::Rc; -use std::sync::Arc; - -use identity_iota::account::Account; -use identity_iota::account::AccountBuilder; -use identity_iota::account::PublishOptions; -use identity_iota::account_storage::CekAlgorithm; -use identity_iota::account_storage::EncryptedData; -use identity_iota::account_storage::EncryptionAlgorithm; -use identity_iota::account_storage::Storage; -use identity_iota::client::Client; -use identity_iota::core::OneOrMany; -use identity_iota::credential::Credential; -use identity_iota::credential::Presentation; -use identity_iota::crypto::ProofOptions; -use identity_iota::crypto::PublicKey; -use identity_iota::did::verifiable::VerifiableProperties; -use identity_iota::iota::IotaDID; -use identity_iota::iota::IotaDocument; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::types::WasmAutoSave; -use crate::account::types::WasmCekAlgorithm; -use crate::account::types::WasmEncryptedData; -use crate::account::types::WasmEncryptionAlgorithm; -use crate::common::PromiseVoid; -use crate::common::UOneOrManyNumber; -use crate::credential::WasmCredential; -use crate::credential::WasmPresentation; -use crate::crypto::WasmProofOptions; -use crate::did::PromiseResolvedDocument; -use crate::did::WasmDocument; -use crate::did::WasmIotaDID; -use crate::did::WasmResolvedDocument; -use crate::error::Result; -use crate::error::WasmResult; - -pub(crate) type AccountRc = Account>; - -/// An account manages one identity. -/// -/// It handles private keys, writing to storage and -/// publishing to the Tangle. -#[wasm_bindgen(js_name = Account)] -pub struct WasmAccount(pub(crate) Rc>); - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Returns the {@link IotaDID} of the managed identity. - #[wasm_bindgen(js_name = did)] - pub fn did(&self) -> WasmIotaDID { - WasmIotaDID::from(self.0.borrow().did().clone()) - } - - /// Returns whether auto-publish is enabled. - #[wasm_bindgen] - pub fn autopublish(&self) -> bool { - self.0.borrow().autopublish() - } - - /// Returns the auto-save configuration value. - #[wasm_bindgen] - pub fn autosave(&self) -> WasmAutoSave { - WasmAutoSave(self.0.borrow().autosave()) - } - - /// Returns a copy of the document managed by the `Account`. - /// - /// Note: the returned document only has a valid signature after publishing an integration chain update. - /// In general, for use cases where the signature is required, it is advisable to resolve the - /// document from the Tangle. - #[wasm_bindgen] - pub fn document(&self) -> WasmDocument { - let document: IotaDocument = self.0.borrow().document().clone(); - WasmDocument::from(document) - } - - /// Resolves the DID Document associated with this `Account` from the Tangle. - #[wasm_bindgen(js_name = resolveIdentity)] - pub fn resolve_identity(&self) -> PromiseResolvedDocument { - let account: Rc> = self.0.clone(); - - let promise: Promise = future_to_promise(async move { - account - .as_ref() - .borrow() - .resolve_identity() - .await - .map(WasmResolvedDocument::from) - .map(Into::into) - .wasm_result() - }); - promise.unchecked_into::() - } - - /// Removes the identity from the local storage entirely. - /// - /// Note: This will remove all associated document updates and key material - recovery is NOT POSSIBLE! - #[wasm_bindgen(js_name = deleteIdentity)] - pub fn delete_identity(self) -> PromiseVoid { - // Get IotaDID and storage from the account. - let did: IotaDID = self.0.borrow().did().to_owned(); - let storage: Arc = Arc::clone(self.0.borrow().storage()); - - future_to_promise(async move { - // Create a new account since `delete_identity` consumes it. - let account: Result = AccountBuilder::new() - .storage_shared(storage) - .load_identity(did) - .await - .wasm_result(); - - match account { - Ok(a) => a.delete_identity().await.wasm_result().map(|_| JsValue::undefined()), - Err(e) => Err(e), - } - }) - .unchecked_into::() - } - - /// Push all unpublished changes to the tangle in a single message. - #[wasm_bindgen] - pub fn publish(&mut self, publish_options: Option) -> PromiseVoid { - let options: PublishOptions = publish_options.map(PublishOptions::from).unwrap_or_default(); - let account = self.0.clone(); - future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .publish_with_options(options) - .await - .map(|_| JsValue::undefined()) - .wasm_result() - }) - .unchecked_into::() - } - - /// Signs a {@link Credential} with the key specified by `fragment`. - #[wasm_bindgen(js_name = createSignedCredential)] - pub fn create_signed_credential( - &self, - fragment: String, - credential: &WasmCredential, - options: &WasmProofOptions, - ) -> PromiseCredential { - let account = self.0.clone(); - let options: ProofOptions = options.0.clone(); - - let mut credential: Credential = credential.0.clone(); - future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .sign(fragment.as_str(), &mut credential, options) - .await - .wasm_result()?; - Ok(JsValue::from(WasmCredential::from(credential))) - }) - .unchecked_into::() - } - - /// Signs a {@link Document} with the key specified by `fragment`. - #[wasm_bindgen(js_name = createSignedDocument)] - pub fn create_signed_document( - &self, - fragment: String, - document: &WasmDocument, - options: &WasmProofOptions, - ) -> PromiseDocument { - let account = self.0.clone(); - let options: ProofOptions = options.0.clone(); - - let mut document: IotaDocument = document.0.clone(); - future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .sign(fragment.as_str(), &mut document, options) - .await - .wasm_result()?; - Ok(JsValue::from(WasmDocument::from(document))) - }) - .unchecked_into::() - } - - /// Signs a {@link Presentation} the key specified by `fragment`. - #[wasm_bindgen(js_name = createSignedPresentation)] - pub fn create_signed_presentation( - &self, - fragment: String, - presentation: &WasmPresentation, - options: &WasmProofOptions, - ) -> PromisePresentation { - let account = self.0.clone(); - let options: ProofOptions = options.0.clone(); - - let mut presentation: Presentation = presentation.0.clone(); - future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .sign(fragment.as_str(), &mut presentation, options) - .await - .wasm_result()?; - Ok(JsValue::from(WasmPresentation::from(presentation))) - }) - .unchecked_into::() - } - - /// Signs arbitrary `data` with the key specified by `fragment`. - #[wasm_bindgen(js_name = createSignedData)] - pub fn create_signed_data(&self, fragment: String, data: &JsValue, options: &WasmProofOptions) -> Result { - let mut verifiable_properties: VerifiableProperties = data.into_serde().wasm_result()?; - let account = self.0.clone(); - let options: ProofOptions = options.0.clone(); - - let promise = future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .sign(fragment.as_str(), &mut verifiable_properties, options) - .await - .map(|_| JsValue::undefined()) - .wasm_result()?; - JsValue::from_serde(&verifiable_properties).wasm_result() - }); - Ok(promise) - } - - /// Overwrites the {@link Document} this account manages, **without doing any validation**. - /// - /// ### WARNING - /// - /// This method is dangerous and can easily corrupt the internal state, - /// potentially making the identity unusable. Only call this if you fully - /// understand the implications! - #[wasm_bindgen(js_name = updateDocumentUnchecked)] - pub fn update_document_unchecked(&mut self, document: &WasmDocument) -> PromiseVoid { - let account = self.0.clone(); - let document_copy: IotaDocument = document.0.clone(); - future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .update_document_unchecked(document_copy) - .await - .map(|_| JsValue::undefined()) - .wasm_result() - }) - .unchecked_into::() - } - - /// Fetches the latest changes from the tangle and **overwrites** the local document. - /// - /// If a DID is managed from distributed accounts, this should be called before making changes - /// to the identity, to avoid publishing updates that would be ignored. - #[wasm_bindgen(js_name = fetchDocument)] - pub fn fetch_document(&mut self) -> PromiseVoid { - let account = self.0.clone(); - future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .fetch_document() - .await - .map(|_| JsValue::undefined()) - .wasm_result() - }) - .unchecked_into::() - } - - /// If the document has a `RevocationBitmap` service identified by `fragment`, - /// revoke all specified `indices`. - #[wasm_bindgen(js_name = revokeCredentials)] - #[allow(non_snake_case)] - pub fn revoke_credentials(&mut self, fragment: String, indices: UOneOrManyNumber) -> PromiseVoid { - let account = self.0.clone(); - future_to_promise(async move { - let indices: OneOrMany = indices.into_serde().wasm_result()?; - - account - .as_ref() - .borrow_mut() - .revoke_credentials(&fragment, indices.as_slice()) - .await - .map(|_| JsValue::undefined()) - .wasm_result() - }) - .unchecked_into::() - } - - /// If the document has a `RevocationBitmap` service identified by `fragment`, - /// unrevoke all specified `indices`. - #[wasm_bindgen(js_name = unrevokeCredentials)] - #[allow(non_snake_case)] - pub fn unrevoke_credentials(&mut self, fragment: String, indices: UOneOrManyNumber) -> PromiseVoid { - let account = self.0.clone(); - future_to_promise(async move { - let indices: OneOrMany = indices.into_serde().wasm_result()?; - - account - .as_ref() - .borrow_mut() - .unrevoke_credentials(&fragment, indices.as_slice()) - .await - .map(|_| JsValue::undefined()) - .wasm_result() - }) - .unchecked_into::() - } - - /// Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`. - /// - /// Returns an [`EncryptedData`] instance. - #[wasm_bindgen(js_name = encryptData)] - pub fn encrypt_data( - &self, - plaintext: Vec, - associated_data: Vec, - encryption_algorithm: &WasmEncryptionAlgorithm, - cek_algorithm: &WasmCekAlgorithm, - public_key: Vec, - ) -> PromiseEncryptedData { - let account = self.0.clone(); - let encryption_algorithm: EncryptionAlgorithm = encryption_algorithm.0; - let cek_algorithm: CekAlgorithm = cek_algorithm.0.clone(); - let public_key: PublicKey = public_key.to_vec().into(); - - future_to_promise(async move { - let encrypted_data: EncryptedData = account - .as_ref() - .borrow() - .encrypt_data( - &plaintext, - &associated_data, - &encryption_algorithm, - &cek_algorithm, - public_key, - ) - .await - .wasm_result()?; - Ok(JsValue::from(WasmEncryptedData::from(encrypted_data))) - }) - .unchecked_into::() - } - - /// Decrypts the given `data` with the key identified by `fragment` using the given `encryption_algorithm` and - /// `cek_algorithm`. - /// - /// Returns the decrypted text. - #[wasm_bindgen(js_name = decryptData)] - pub fn decrypt_data( - &self, - data: &WasmEncryptedData, - encryption_algorithm: &WasmEncryptionAlgorithm, - cek_algorithm: &WasmCekAlgorithm, - fragment: String, - ) -> PromiseData { - let account = self.0.clone(); - let data: EncryptedData = data.0.clone(); - let encryption_algorithm: EncryptionAlgorithm = encryption_algorithm.0; - let cek_algorithm: CekAlgorithm = cek_algorithm.0.clone(); - - future_to_promise(async move { - let data: Vec = account - .as_ref() - .borrow() - .decrypt_data(data, &encryption_algorithm, &cek_algorithm, &fragment) - .await - .wasm_result()?; - Ok(JsValue::from(js_sys::Uint8Array::from(data.as_ref()))) - }) - .unchecked_into::() - } -} - -impl From for WasmAccount { - fn from(account: AccountRc) -> WasmAccount { - WasmAccount(Rc::new(RefCell::new(account))) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseCredential; - - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromisePresentation; - - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseDocument; - - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseEncryptedData; - - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseData; -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "PublishOptions")] - pub type WasmPublishOptions; - - #[wasm_bindgen(getter, method)] - pub fn forceIntegrationUpdate(this: &WasmPublishOptions) -> Option; - - #[wasm_bindgen(getter, method)] - pub fn signWith(this: &WasmPublishOptions) -> Option; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_PUBLISH_OPTIONS: &'static str = r#" -/** - * Options to customize how identities are published to the Tangle. -**/ -export type PublishOptions = { - /** - * Whether to force the publication to be an integration update. - * If this option is not set, the account automatically determines whether - * an update needs to be published as an integration or a diff update. - * Publishing as an integration update is always valid, but not recommended - * for identities with many updates. - * - * See the IOTA DID method specification for more details. - * - * @deprecated since 0.5.0, diff chain features are slated for removal. - */ - forceIntegrationUpdate?: boolean, - - - /** - * Set the fragment of a verification method with which to sign the update. - * This must point to an Ed25519 method with a capability invocation - * verification relationship. - * - * If omitted, the default signing method on the Document will be used. - */ - signWith?: string - } -"#; - -impl From for PublishOptions { - fn from(publish_options: WasmPublishOptions) -> Self { - let mut options: PublishOptions = PublishOptions::new(); - - if let Some(force_integration) = publish_options.forceIntegrationUpdate() { - options = options.force_integration_update(force_integration); - } - - if let Some(sign_with) = publish_options.signWith() { - options = options.sign_with(sign_with); - }; - options - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseAccount; -} diff --git a/bindings/wasm/src/account/wasm_account/account_builder.rs b/bindings/wasm/src/account/wasm_account/account_builder.rs deleted file mode 100644 index 9fb722cdf9..0000000000 --- a/bindings/wasm/src/account/wasm_account/account_builder.rs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cell::RefCell; -use std::rc::Rc; - -use identity_iota::account::AccountBuilder; -use identity_iota::account::IdentitySetup; -use identity_iota::client::Client; -use identity_iota::client::ClientBuilder; -use identity_iota::iota::IotaDID; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::storage::WasmStorage; -use crate::account::types::OptionAutoSave; -use crate::account::types::WasmIdentitySetup; -use crate::account::wasm_account::PromiseAccount; -use crate::account::wasm_account::WasmAccount; -use crate::did::WasmIotaDID; -use crate::error::Result; -use crate::error::WasmResult; -use crate::tangle::IClientConfig; - -type AccountBuilderRc = AccountBuilder>; - -/// An [`Account`] builder for easy account configuration. -/// -/// To reduce memory usage, accounts created from the same builder share the same `Storage` -/// used to store identities, and the same {@link Client} used to publish identities to the Tangle. -/// -/// The configuration on the other hand is cloned, and therefore unique for each built account. -/// This means a builder can be reconfigured in-between account creations, without affecting -/// the configuration of previously built accounts. -#[wasm_bindgen(js_name = AccountBuilder)] -pub struct WasmAccountBuilder(Rc>); - -#[wasm_bindgen(js_class = AccountBuilder)] -impl WasmAccountBuilder { - /// Creates a new `AccountBuilder`. - #[wasm_bindgen(constructor)] - pub fn new(options: Option) -> Result { - let mut builder: AccountBuilderRc = AccountBuilderRc::new(); - - if let Some(builder_options) = options { - if let Some(autopublish) = builder_options.autopublish() { - builder = builder.autopublish(autopublish); - } - - if let Some(autosave) = builder_options.autosave().into_serde().wasm_result()? { - builder = builder.autosave(autosave); - } - - if let Some(config) = builder_options.clientConfig() { - let client_builder: ClientBuilder = ClientBuilder::try_from(config)?; - builder = builder.client_builder(client_builder); - }; - - if let Some(storage) = builder_options.storage() { - builder = builder.storage(storage); - } - } - - Ok(Self(Rc::new(RefCell::new(builder)))) - } - - /// Loads an existing identity with the specified `did` using the current builder configuration. - /// The identity must exist in the configured `Storage`. - #[wasm_bindgen(js_name = loadIdentity)] - pub fn load_identity(&mut self, did: &WasmIotaDID) -> Result { - let builder: Rc> = self.0.clone(); - let did: IotaDID = did.0.clone(); - let promise: Promise = future_to_promise(async move { - builder - .as_ref() - .borrow_mut() - .load_identity(did) - .await - .map(WasmAccount::from) - .map(Into::into) - .wasm_result() - }); - Ok(promise.unchecked_into::()) - } - - /// Creates a new identity based on the builder configuration and returns - /// an {@link Account} object to manage it. - /// - /// The identity is stored locally in the `Storage`. The DID network is automatically determined - /// by the {@link Client} used to publish it. - #[wasm_bindgen(js_name = createIdentity)] - pub fn create_identity(&mut self, identity_setup: Option) -> Result { - let setup: IdentitySetup = identity_setup.map(IdentitySetup::from).unwrap_or_default(); - - let builder: Rc> = self.0.clone(); - let promise: Promise = future_to_promise(async move { - builder - .as_ref() - .borrow_mut() - .create_identity(setup) - .await - .map(WasmAccount::from) - .map(Into::into) - .wasm_result() - }); - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "AccountBuilderOptions")] - pub type AccountBuilderOptions; - - #[wasm_bindgen(getter, method)] - pub fn autopublish(this: &AccountBuilderOptions) -> Option; - - #[wasm_bindgen(getter, method)] - pub fn clientConfig(this: &AccountBuilderOptions) -> Option; - - #[wasm_bindgen(getter, method)] - pub fn milestone(this: &AccountBuilderOptions) -> Option; - - #[wasm_bindgen(getter, method)] - pub fn autosave(this: &AccountBuilderOptions) -> OptionAutoSave; - - #[wasm_bindgen(getter, method)] - pub fn storage(this: &AccountBuilderOptions) -> Option; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_ACCOUNT_BUILDER_OPTIONS: &'static str = r#" -/** - * Options for creating a new {@link AccountBuilder}. - */ -export type AccountBuilderOptions = { - - /** - * When the account will store its state to the storage. - */ - autosave?: AutoSave - - /** - * `autopublish == true` the account will publish messages to the tangle on each update. - * `autopublish == false` the account will combine and publish message when .publish() is called. - */ - autopublish?: boolean, - - /** - * Client for tangle requests. - */ - clientConfig?: IClientConfig, - - /** - * The Storage implemantation to use for each account built by this builder. - */ - storage?: Storage -}; -"#; diff --git a/bindings/wasm/src/account/wasm_account/mod.rs b/bindings/wasm/src/account/wasm_account/mod.rs deleted file mode 100644 index 3fd26934fa..0000000000 --- a/bindings/wasm/src/account/wasm_account/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use self::account::PromiseAccount; -pub use self::account::WasmAccount; -pub use self::account_builder::WasmAccountBuilder; - -mod account; -mod account_builder; -mod update; diff --git a/bindings/wasm/src/account/wasm_account/update/attach_method_relationships.rs b/bindings/wasm/src/account/wasm_account/update/attach_method_relationships.rs deleted file mode 100644 index 99de28c8ce..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/attach_method_relationships.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cell::RefCell; -use std::rc::Rc; - -use identity_iota::account::UpdateError::MissingRequiredField; -use identity_iota::core::OneOrMany; -use identity_iota::did::MethodRelationship; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::wasm_account::account::AccountRc; -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::did::WasmMethodRelationship; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Attach one or more verification relationships to a method. - /// - /// Note: the method must exist and be in the set of verification methods; - /// it cannot be an embedded method. - #[wasm_bindgen(js_name = attachMethodRelationships)] - pub fn attach_method_relationships(&mut self, options: &AttachMethodRelationshipOptions) -> Result { - let relationships: Vec = options - .relationships() - .into_serde::>() - .map(OneOrMany::into_vec) - .wasm_result()? - .into_iter() - .map(MethodRelationship::from) - .collect(); - - let fragment: String = options - .fragment() - .ok_or(MissingRequiredField("fragment")) - .wasm_result()?; - - let account: Rc> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - if relationships.is_empty() { - return Ok(JsValue::undefined()); - } - account - .borrow_mut() - .update_identity() - .attach_method_relationship() - .fragment(fragment) - .relationships(relationships) - .apply() - .await - .wasm_result() - .map(|_| JsValue::undefined()) - }); - - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "AttachMethodRelationshipOptions")] - pub type AttachMethodRelationshipOptions; - - #[wasm_bindgen(getter, method)] - pub fn fragment(this: &AttachMethodRelationshipOptions) -> Option; - - #[wasm_bindgen(getter, method)] - pub fn relationships(this: &AttachMethodRelationshipOptions) -> JsValue; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_ATTACH_METHOD_RELATIONSHIP_OPTIONS: &'static str = r#" -/** - * Options for attaching one or more verification relationships to a method on an identity. - */ -export type AttachMethodRelationshipOptions = { - /** - * The identifier of the method in the document. - */ - fragment: string, - - /** - * The relationships to add; - */ - relationships: MethodRelationship | MethodRelationship[] -}; -"#; diff --git a/bindings/wasm/src/account/wasm_account/update/create_method.rs b/bindings/wasm/src/account/wasm_account/update/create_method.rs deleted file mode 100644 index d84bdebaa3..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/create_method.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cell::RefCell; -use std::cell::RefMut; -use std::rc::Rc; - -use identity_iota::account::CreateMethodBuilder; -use identity_iota::account::IdentityUpdater; -use identity_iota::account::MethodContent; -use identity_iota::account::UpdateError::MissingRequiredField; -use identity_iota::client::Client; -use identity_iota::did::MethodScope; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::types::OptionMethodContent; -use crate::account::types::WasmMethodContent; -use crate::account::wasm_account::account::AccountRc; -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::did::OptionMethodScope; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Adds a new verification method to the DID document. - #[wasm_bindgen(js_name = createMethod)] - pub fn create_method(&mut self, options: &CreateMethodOptions) -> Result { - let fragment: String = options - .fragment() - .ok_or(MissingRequiredField("fragment")) - .wasm_result()?; - let scope: Option = options.scope().into_serde().wasm_result()?; - let content: MethodContent = options - .content() - .into_serde::>() - .wasm_result()? - .map(MethodContent::from) - .ok_or(MissingRequiredField("content")) - .wasm_result()?; - - let account: Rc> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - let mut account: RefMut = account.borrow_mut(); - let mut updater: IdentityUpdater<'_, Rc> = account.update_identity(); - - let mut create_method: CreateMethodBuilder<'_, Rc> = - updater.create_method().content(content).fragment(fragment); - if let Some(scope) = scope { - create_method = create_method.scope(scope); - }; - - create_method.apply().await.wasm_result().map(|_| JsValue::undefined()) - }); - - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "CreateMethodOptions")] - pub type CreateMethodOptions; - - #[wasm_bindgen(getter, method)] - pub fn fragment(this: &CreateMethodOptions) -> Option; - - #[wasm_bindgen(getter, method)] - pub fn scope(this: &CreateMethodOptions) -> OptionMethodScope; - - #[wasm_bindgen(getter, method)] - pub fn content(this: &CreateMethodOptions) -> OptionMethodContent; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_CREATE_METHOD_OPTIONS: &'static str = r#" -/** - * Options for creating a new method on an identity. - */ -export type CreateMethodOptions = { - /** - * The identifier of the method in the document. - */ - fragment: string, - - /** - * The scope of the method, defaults to VerificationMethod. - */ - scope?: MethodScope, - - /** - * Method content for the new method. - */ - content: MethodContent - }; -"#; diff --git a/bindings/wasm/src/account/wasm_account/update/create_service.rs b/bindings/wasm/src/account/wasm_account/update/create_service.rs deleted file mode 100644 index b1a2eafe5f..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/create_service.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cell::RefCell; -use std::cell::RefMut; -use std::rc::Rc; - -use identity_iota::account::CreateServiceBuilder; -use identity_iota::account::IdentityUpdater; -use identity_iota::account::UpdateError::MissingRequiredField; -use identity_iota::client::Client; -use identity_iota::core::Object; -use identity_iota::core::OneOrSet; -use identity_iota::did::ServiceEndpoint; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::wasm_account::account::AccountRc; -use crate::account::wasm_account::WasmAccount; -use crate::common::deserialize_map_or_any; -use crate::common::PromiseVoid; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Adds a new Service to the DID Document. - #[wasm_bindgen(js_name = createService)] - pub fn create_service(&mut self, options: &CreateServiceOptions) -> Result { - let fragment: String = options - .fragment() - .ok_or(MissingRequiredField("fragment")) - .wasm_result()?; - let service_types: OneOrSet = options.type_().into_serde().wasm_result()?; - let endpoint: ServiceEndpoint = deserialize_map_or_any(&options.endpoint())?; - let properties: Option = deserialize_map_or_any(&options.properties())?; - - let account: Rc> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - let mut account: RefMut = account.borrow_mut(); - let mut updater: IdentityUpdater<'_, Rc> = account.update_identity(); - let mut create_service: CreateServiceBuilder<'_, Rc> = updater - .create_service() - .fragment(fragment) - .types(service_types) - .endpoint(endpoint); - - if let Some(properties) = properties { - create_service = create_service.properties(properties) - } - - create_service.apply().await.wasm_result().map(|_| JsValue::undefined()) - }); - - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "CreateServiceOptions")] - pub type CreateServiceOptions; - - #[wasm_bindgen(getter, method)] - pub fn fragment(this: &CreateServiceOptions) -> Option; - - #[wasm_bindgen(getter, method, js_name = type)] - pub fn type_(this: &CreateServiceOptions) -> JsValue; - - #[wasm_bindgen(getter, method)] - pub fn endpoint(this: &CreateServiceOptions) -> JsValue; - - #[wasm_bindgen(getter, method)] - pub fn properties(this: &CreateServiceOptions) -> JsValue; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_CREATE_SERVICE_OPTIONS: &'static str = r#" -/** - * Options for creating a new service on an identity. - */ -export type CreateServiceOptions = { - /** - * The identifier of the service in the document. - */ - fragment: string; - - /** - * The type of the service. - */ - type: string | string[]; - - /** - * The `ServiceEndpoint` of the service. - */ - endpoint: string | string[] | Map | Record; - - /** - * Additional properties of the service. - */ - properties?: Map | Record; -}; -"#; diff --git a/bindings/wasm/src/account/wasm_account/update/delete_method.rs b/bindings/wasm/src/account/wasm_account/update/delete_method.rs deleted file mode 100644 index 1d76ae72c7..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/delete_method.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account::UpdateError::MissingRequiredField; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Deletes a verification method if the method exists. - #[wasm_bindgen(js_name = deleteMethod)] - pub fn delete_method(&mut self, options: &DeleteMethodOptions) -> Result { - let fragment: String = options - .fragment() - .ok_or(MissingRequiredField("fragment")) - .wasm_result()?; - let account = self.0.clone(); - let promise: Promise = future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .update_identity() - .delete_method() - .fragment(fragment) - .apply() - .await - .wasm_result() - .map(|_| JsValue::undefined()) - }); - - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "DeleteMethodOptions")] - pub type DeleteMethodOptions; - - #[wasm_bindgen(getter, method)] - pub fn fragment(this: &DeleteMethodOptions) -> Option; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_DELETE_METHOD_OPTIONS: &'static str = r#" -/** - * Options for deleting a method on an identity. - */ -export type DeleteMethodOptions = { - /** - * The identifier of the method in the document. - */ - fragment: string, -}; -"#; diff --git a/bindings/wasm/src/account/wasm_account/update/delete_service.rs b/bindings/wasm/src/account/wasm_account/update/delete_service.rs deleted file mode 100644 index 11194d856e..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/delete_service.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota::account::UpdateError::MissingRequiredField; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Deletes a Service if it exists. - #[wasm_bindgen(js_name = deleteService)] - pub fn delete_service(&mut self, options: &DeleteServiceOptions) -> Result { - let fragment: String = options - .fragment() - .ok_or(MissingRequiredField("fragment")) - .wasm_result()?; - - let account = self.0.clone(); - - let promise: Promise = future_to_promise(async move { - account - .as_ref() - .borrow_mut() - .update_identity() - .delete_service() - .fragment(fragment) - .apply() - .await - .wasm_result() - .map(|_| JsValue::undefined()) - }); - - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "DeleteServiceOptions")] - pub type DeleteServiceOptions; - - #[wasm_bindgen(getter, method)] - pub fn fragment(this: &DeleteServiceOptions) -> Option; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_DELETE_SERVICE_OPTIONS: &'static str = r#" -/** - * Options for deleting a service on an identity. - */ -export type DeleteServiceOptions = { - /** - * The identifier of the service in the document. - */ - fragment: string, -}; -"#; diff --git a/bindings/wasm/src/account/wasm_account/update/detach_method_relationships.rs b/bindings/wasm/src/account/wasm_account/update/detach_method_relationships.rs deleted file mode 100644 index 7dc3961f2f..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/detach_method_relationships.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cell::RefCell; -use std::rc::Rc; - -use identity_iota::account::UpdateError::MissingRequiredField; -use identity_iota::core::OneOrMany; -use identity_iota::did::MethodRelationship; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::wasm_account::account::AccountRc; -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::did::WasmMethodRelationship; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Detaches the given relationship from the given method, if the method exists. - #[wasm_bindgen(js_name = detachMethodRelationships)] - pub fn detach_method_relationships(&mut self, options: &DetachMethodRelationshipOptions) -> Result { - let relationships: Vec = options - .relationships() - .into_serde::>() - .map(OneOrMany::into_vec) - .wasm_result()? - .into_iter() - .map(MethodRelationship::from) - .collect(); - - let fragment: String = options - .fragment() - .ok_or(MissingRequiredField("fragment")) - .wasm_result()?; - - let account: Rc> = Rc::clone(&self.0); - - let promise: Promise = future_to_promise(async move { - if relationships.is_empty() { - return Ok(JsValue::undefined()); - } - account - .borrow_mut() - .update_identity() - .detach_method_relationship() - .fragment(fragment) - .relationships(relationships) - .apply() - .await - .wasm_result() - .map(|_| JsValue::undefined()) - }); - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "DetachMethodRelationshipOptions")] - pub type DetachMethodRelationshipOptions; - - #[wasm_bindgen(getter, method)] - pub fn fragment(this: &DetachMethodRelationshipOptions) -> Option; - - #[wasm_bindgen(getter, method)] - pub fn relationships(this: &DetachMethodRelationshipOptions) -> JsValue; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_DETACH_METHOD_RELATIONSHIP_OPTIONS: &'static str = r#" -/** - * Options for detaching one or more verification relationships from a method on an identity. - */ -export type DetachMethodRelationshipOptions = { - /** - * The identifier of the method in the document. - */ - fragment: string, - - /** - * The relationships to remove. - */ - relationships: MethodRelationship | MethodRelationship[] -}; -"#; diff --git a/bindings/wasm/src/account/wasm_account/update/mod.rs b/bindings/wasm/src/account/wasm_account/update/mod.rs deleted file mode 100644 index 71f3b5d6de..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod attach_method_relationships; -mod create_method; -mod create_service; -mod delete_method; -mod delete_service; -mod detach_method_relationships; -mod set_also_known_as; -mod set_controller; diff --git a/bindings/wasm/src/account/wasm_account/update/set_also_known_as.rs b/bindings/wasm/src/account/wasm_account/update/set_also_known_as.rs deleted file mode 100644 index 612bdef687..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/set_also_known_as.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cell::RefCell; -use std::rc::Rc; - -use identity_iota::core::OneOrMany; -use identity_iota::core::OrderedSet; -use identity_iota::core::Url; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::wasm_account::account::AccountRc; -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Sets the `alsoKnownAs` property in the DID document. - #[wasm_bindgen(js_name = setAlsoKnownAs)] - pub fn set_also_known_as(&mut self, options: &SetAlsoKnownAsOptions) -> Result { - let urls: Option> = options.urls().into_serde::>>().wasm_result()?; - - let mut urls_set: OrderedSet = OrderedSet::new(); - if let Some(urls) = urls { - for url in urls.into_vec() { - urls_set.append(Url::parse(url).wasm_result()?); - } - } - - let account: Rc> = Rc::clone(&self.0); - let promise: Promise = future_to_promise(async move { - account - .borrow_mut() - .update_identity() - .set_also_known_as() - .urls(urls_set) - .apply() - .await - .wasm_result() - .map(|_| JsValue::undefined()) - }); - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "SetAlsoKnownAsOptions")] - pub type SetAlsoKnownAsOptions; - - #[wasm_bindgen(getter, method)] - pub fn urls(this: &SetAlsoKnownAsOptions) -> JsValue; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_SET_ALSO_KNOWN_AS_OPTIONS: &'static str = r#" -/** - * Options for setting the `alsoKnownAs` property. - */ - export type SetAlsoKnownAsOptions = { - - /** - * List of URLs for the `alsoKnownAs` property. Duplicates are ignored. - */ - urls: string | string[] | null, -}; -"#; diff --git a/bindings/wasm/src/account/wasm_account/update/set_controller.rs b/bindings/wasm/src/account/wasm_account/update/set_controller.rs deleted file mode 100644 index f7cd06069b..0000000000 --- a/bindings/wasm/src/account/wasm_account/update/set_controller.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cell::RefCell; -use std::rc::Rc; - -use identity_iota::core::OneOrMany; -use identity_iota::core::OneOrSet; -use identity_iota::core::OrderedSet; -use identity_iota::iota::IotaDID; -use js_sys::Promise; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::future_to_promise; - -use crate::account::wasm_account::account::AccountRc; -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::error::Result; -use crate::error::WasmResult; - -#[wasm_bindgen(js_class = Account)] -impl WasmAccount { - /// Sets the controllers of the DID document. - #[wasm_bindgen(js_name = setController)] - pub fn set_controller(&mut self, options: &SetControllerOptions) -> Result { - let controllers: Option> = options.controllers().into_serde().wasm_result()?; - - let controller_set: Option> = if let Some(controllers) = controllers { - match controllers { - OneOrMany::One(controller) => Some(OneOrSet::new_one(controller)), - OneOrMany::Many(controllers) => { - if controllers.is_empty() { - None - } else { - let mut set: OrderedSet = OrderedSet::new(); - for controller in controllers { - set.append(controller); - } - Some(OneOrSet::new_set(set).wasm_result()?) - } - } - } - } else { - None - }; - - let account: Rc> = Rc::clone(&self.0); - - let promise: Promise = future_to_promise(async move { - account - .borrow_mut() - .update_identity() - .set_controller() - .controllers(controller_set) - .apply() - .await - .wasm_result() - .map(|_| JsValue::undefined()) - }); - Ok(promise.unchecked_into::()) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "SetControllerOptions")] - pub type SetControllerOptions; - - #[wasm_bindgen(getter, method)] - pub fn controllers(this: &SetControllerOptions) -> JsValue; -} - -#[wasm_bindgen(typescript_custom_section)] -const TS_SET_CONTROLLER_OPTIONS: &'static str = r#" -/** - * Options for setting DID controllers. - */ - export type SetControllerOptions = { - - /** - * List of DIDs to be set as controllers, use `null` to remove all controllers. - */ - controllers: IotaDID | IotaDID[] | null, -}; -"#; diff --git a/documentation/docs/concepts/verifiable_credentials/create.mdx b/documentation/docs/concepts/verifiable_credentials/create.mdx index 8e85f1beb9..5e8a932cb0 100644 --- a/documentation/docs/concepts/verifiable_credentials/create.mdx +++ b/documentation/docs/concepts/verifiable_credentials/create.mdx @@ -9,7 +9,8 @@ keywords: - Create - sign --- -import create_vc_rs from '!!raw-loader!../../../../examples_legacy/account/create_vc.rs'; +import create_vc_ts from '!!raw-loader!../../../../bindings/wasm/examples/src/0_basic/5_create_vc.ts' +import create_vc_rs from '!!raw-loader!../../../../examples/0_basic/5_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. @@ -79,8 +80,8 @@ The following code exemplifies how an issuer can create, sign, and validate a ve This Verifiable Credential can be [verified by anyone](./verifiable_presentations.mdx), allowing Alice to take control of it and share it with anyone. \ 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 baa1aa4f50..a9e5565541 100644 --- a/documentation/docs/concepts/verifiable_credentials/revocation.mdx +++ b/documentation/docs/concepts/verifiable_credentials/revocation.mdx @@ -9,8 +9,8 @@ keywords: - revoke - revocation --- - -import revoke_vc_rs from "!!raw-loader!../../../../examples_legacy/account/revoke_vc.rs"; +import revoke_vc_ts from '!!raw-loader!../../../../bindings/wasm/examples/src/0_basic/7_revoke_vc.ts' +import revoke_vc_rs from "!!raw-loader!../../../../examples/0_basic/7_revoke_vc.rs"; import CodeSnippet from "../../../src/components/CodeSnippetComponent"; ## Overview @@ -55,8 +55,8 @@ A less efficient alternative is to remove the verification method that signed th The following code exemplifies how you can revoke a [Verifiable Credential (VC)](overview). diff --git a/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx b/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx index ff3a86504e..50bf9db334 100644 --- a/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx +++ b/documentation/docs/concepts/verifiable_credentials/verifiable_presentations.mdx @@ -7,7 +7,8 @@ keywords: - verifiable - presentations --- -import create_vp_rs from '!!raw-loader!../../../../examples_legacy/account/create_vp.rs'; +import create_vp_ts from '!!raw-loader!../../../../bindings/wasm/examples/src/0_basic/6_create_vp.ts' +import create_vp_rs from '!!raw-loader!../../../../examples/0_basic/6_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). @@ -166,8 +167,8 @@ The following code demonstrates how to use the IOTA Identity Framework end-to-en serialize it to JSON for transmission, deserialize it on the receiving side as a verifier, and finally validate it with various options. diff --git a/examples_legacy/Cargo.toml b/examples_legacy/Cargo.toml deleted file mode 100644 index 5e9aef6c5d..0000000000 --- a/examples_legacy/Cargo.toml +++ /dev/null @@ -1,90 +0,0 @@ -[package] -name = "examples_legacy" -version = "0.7.0-alpha.3" -edition = "2021" -publish = false - -[workspace] - -[dependencies] -identity_account = { path = "../identity_account" } -identity_account_storage = { path = "../identity_account_storage" } -identity_iota = { path = "../identity_iota" } -identity_iota_client_legacy = { path = "../identity_iota_client_legacy" } -identity_iota_core_legacy = { path = "../identity_iota_core_legacy" } -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 deleted file mode 100644 index c0bd008e9c..0000000000 --- a/examples_legacy/README.md +++ /dev/null @@ -1,47 +0,0 @@ -![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_legacy/account/config.rs b/examples_legacy/account/config.rs deleted file mode 100644 index 1151d986c3..0000000000 --- a/examples_legacy/account/config.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_config - -use identity_account::account::Account; -use identity_account::account::AccountBuilder; -use identity_account::account::AutoSave; -use identity_account::types::IdentitySetup; -use identity_account::Result; -use identity_account_storage::storage::MemStore; -use identity_iota_client_legacy::tangle::ClientBuilder; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::tangle::Network; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // Set-up for a private Tangle - // You can use https://github.com/iotaledger/one-click-tangle for a local setup. - // The `network_name` needs to match the id of the network or a part of it. - // As an example we are treating the devnet as a private tangle, so we use `dev`. - // When running the local setup, we can use `tangle` since the id of the one-click - // private tangle is `private-tangle`, but we can only use 6 characters. - // Keep in mind, there are easier ways to change to devnet via `Network::Devnet` - let network_name = "dev"; - let network = Network::try_from_name(network_name)?; - - // If you deployed an explorer locally this would usually be `http://127.0.0.1:8082` - let explorer = ExplorerUrl::parse("https://explorer.iota.org/devnet")?; - - // In a locally running one-click tangle, this would usually be `http://127.0.0.1:14265` - let private_node_url = "https://api.lb-0.h.chrysalis-devnet.iota.cafe"; - - // Create a new Account with explicit configuration - let mut builder: AccountBuilder = Account::builder() - .autosave(AutoSave::Never) // never auto-save. rely on the drop save - .autosave(AutoSave::Every) // save immediately after every action - .autosave(AutoSave::Batch(10)) // save after every 10 actions - .autopublish(true) // publish to the tangle automatically on every update - .storage(MemStore::new()) // use the default in-memory storage - .client_builder( - // Configure a client for the private network - ClientBuilder::new() - .network(network.clone()) - .primary_node(private_node_url, None, None)?, - // set a permanode for the same network - // .permanode(, None, None)? - ); - - // Create an identity and publish it. - // The created DID will use the network name configured for the client. - let identity: Account = match builder.create_identity(IdentitySetup::default()).await { - Ok(identity) => identity, - Err(err) => { - eprintln!("[Example] Error: {:?}", err); - eprintln!("[Example] Is your Tangle node listening on {}?", private_node_url); - return Ok(()); - } - }; - - // Prints the Identity Resolver Explorer URL. - // The entire history can be observed on this page by clicking "Loading History". - let iota_did: &IotaDID = identity.did(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(iota_did)? - ); - - Ok(()) -} diff --git a/examples_legacy/account/create_did.rs b/examples_legacy/account/create_did.rs deleted file mode 100644 index e442b24d60..0000000000 --- a/examples_legacy/account/create_did.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_create - -use std::path::PathBuf; - -use identity_account::account::Account; -use identity_account::types::IdentitySetup; -use identity_account::Result; -use identity_account_storage::stronghold::Stronghold; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_core_legacy::did::IotaDID; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // Sets the location and password for the Stronghold - // - // Stronghold is an encrypted file that manages private keys. - // It implements best practices for security and is the recommended way of handling private keys. - let stronghold_path: PathBuf = "./example-strong.hodl".into(); - let password: String = "my-password".to_owned(); - let stronghold: Stronghold = Stronghold::new(&stronghold_path, password, None).await?; - - // Create a new identity using Stronghold as local storage. - // - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let account: Account = Account::builder() - .storage(stronghold) - .create_identity(IdentitySetup::default()) - .await?; - - // Retrieve the did of the newly created identity. - let iota_did: &IotaDID = account.did(); - - // Print the local state of the DID Document - println!("[Example] Local Document from {} = {:#?}", iota_did, account.document()); - - // Prints the Identity Resolver Explorer URL. - // The entire history can be observed on this page by clicking "Loading History". - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(iota_did)? - ); - - Ok(()) -} diff --git a/examples_legacy/account/create_vc.rs b/examples_legacy/account/create_vc.rs deleted file mode 100644 index 48282ae826..0000000000 --- a/examples_legacy/account/create_vc.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A basic example that generates and publishes subject and issuer DID -//! Documents, then creates a Verifiable Credential (vc) specifying claims about the -//! subject, and retrieves information through the CredentialValidator API. -//! -//! cargo run --example account_create_vc - -use identity_account::account::Account; -use identity_account::account::AccountBuilder; -use identity_account::types::IdentitySetup; -use identity_account::types::MethodContent; -use identity_account::Result; -use identity_iota::core::json; -use identity_iota::core::FromJson; -use identity_iota::core::ToJson; -use identity_iota::core::Url; -use identity_iota::credential::Credential; -use identity_iota::credential::CredentialBuilder; -use identity_iota::credential::CredentialValidationOptions; -use identity_iota::credential::CredentialValidator; -use identity_iota::credential::FailFast; -use identity_iota::credential::Subject; -use identity_iota::crypto::ProofOptions; -use identity_iota::did::DID; - -pub async fn create_vc() -> Result { - // Create an account builder with in-memory storage for simplicity. - // See `create_did` example to configure Stronghold storage. - let mut builder: AccountBuilder = Account::builder(); - - // Create an identity for the issuer. - let mut issuer: Account = builder.create_identity(IdentitySetup::default()).await?; - - // Add verification method to the issuer. - issuer - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("issuerKey") - .apply() - .await?; - - // Create an identity for the holder, in this case also the subject. - let alice: Account = builder.create_identity(IdentitySetup::default()).await?; - - // Create a credential subject indicating the degree earned by Alice. - let subject: Subject = Subject::from_json_value(json!({ - "id": alice.document().id(), - "name": "Alice", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts", - }, - "GPA": "4.0", - }))?; - - // Build credential using subject above and issuer. - let mut credential: Credential = CredentialBuilder::default() - .id(Url::parse("https://example.edu/credentials/3732")?) - .issuer(Url::parse(issuer.did().as_str())?) - .type_("UniversityDegreeCredential") - .subject(subject) - .build()?; - - // Sign the Credential with the issuer's verification method. - issuer - .sign("#issuerKey", &mut credential, ProofOptions::default()) - .await?; - - println!("Credential JSON > {:#}", credential); - - // Before sending this credential to the holder the issuer wants to validate that some properties - // of the credential satisfy their expectations. - - // Validate the credential's signature using the issuer's DID Document, the credential's semantic structure, - // that the issuance date is not in the future and that the expiration date is not in the past: - CredentialValidator::validate( - &credential, - issuer.document(), - &CredentialValidationOptions::default(), - FailFast::FirstError, - ) - .unwrap(); - - println!("VC successfully validated"); - - // The issuer is now sure that the credential they are about to issue satisfies their expectations. - // The credential is then serialized to JSON and transmitted to the subject in a secure manner. - // Note that the credential is NOT published to the IOTA Tangle. It is sent and stored off-chain. - let credential_json: String = credential.to_json()?; - - Ok(credential_json) -} - -#[tokio::main] -async fn main() -> Result<()> { - // Obtain a JSON representation of a credential issued to us - let _credential_json: String = create_vc().await?; - Ok(()) -} diff --git a/examples_legacy/account/create_vp.rs b/examples_legacy/account/create_vp.rs deleted file mode 100644 index 24288208e4..0000000000 --- a/examples_legacy/account/create_vp.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A Verifiable Presentation (VP) represents a bundle of one or more Verifiable Credentials. -//! This example demonstrates building and usage of VPs. -//! -//! cargo run --example account_create_vp - -use identity_account::account::Account; -use identity_account::account::AccountBuilder; -use identity_account::types::IdentitySetup; -use identity_account::types::MethodContent; -use identity_account::Result; -use identity_iota::core::json; -use identity_iota::core::Duration; -use identity_iota::core::FromJson; -use identity_iota::core::Timestamp; -use identity_iota::core::ToJson; -use identity_iota::core::Url; -use identity_iota::credential::Credential; -use identity_iota::credential::CredentialBuilder; -use identity_iota::credential::CredentialValidationOptions; -use identity_iota::credential::FailFast; -use identity_iota::credential::Presentation; -use identity_iota::credential::PresentationBuilder; -use identity_iota::credential::PresentationValidationOptions; -use identity_iota::credential::Subject; -use identity_iota::credential::SubjectHolderRelationship; -use identity_iota::crypto::ProofOptions; -use identity_iota::did::verifiable::VerifierOptions; -use identity_iota_client_legacy::tangle::Resolver; - -#[tokio::main] -async fn main() -> Result<()> { - // =========================================================================== - // Step 1: Create identities for the issuer and the holder. - // =========================================================================== - - // Create an account builder with in-memory storage for simplicity. - // See `create_did` example to configure Stronghold storage. - let mut builder: AccountBuilder = Account::builder(); - - // Create an identity for the issuer. - let mut issuer: Account = builder.create_identity(IdentitySetup::default()).await?; - - // Add a dedicated verification method to the issuer, with which to sign credentials. - issuer - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("issuerKey") - .apply() - .await?; - - // Create an identity for the holder, in this case also the subject. - let mut alice: Account = builder.create_identity(IdentitySetup::default()).await?; - - // Add verification method to the holder. - alice - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("aliceKey") - .apply() - .await?; - - // =========================================================================== - // Step 2: Issuer creates and signs a Verifiable Credential. - // =========================================================================== - - // Create VC "subject" field containing subject ID and claims about it. - let subject: Subject = Subject::from_json_value(json!({ - "id": alice.did().to_string(), - "name": "Alice", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts", - }, - "GPA": "4.0", - }))?; - - // Build credential using subject above and issuer. - let mut credential: Credential = CredentialBuilder::default() - .id(Url::parse("https://example.edu/credentials/3732")?) - .issuer(Url::parse(issuer.did().to_string())?) - .type_("UniversityDegreeCredential") - .subject(subject) - .build()?; - - // Sign the Credential with the issuers default key. - issuer - .sign("#issuerKey", &mut credential, ProofOptions::default()) - .await?; - - println!("Credential JSON > {:#}", credential); - - // =========================================================================== - // Step 3: Issuer sends the Verifiable Credential to the holder. - // =========================================================================== - - // The credential is then serialized to JSON and transmitted to the holder in a secure manner. - // Note that the credential is NOT published to the IOTA Tangle. It is sent and stored off-chain. - let credential_json: String = credential.to_json()?; - - // =========================================================================== - // Step 4: Verifier sends the holder a challenge and requests a signed Verifiable Presentation. - // =========================================================================== - - // A unique random challenge generated by the requester per presentation can mitigate replay attacks - let challenge: &str = "475a7984-1bb5-4c4c-a56f-822bccd46440"; - - // The verifier and holder also agree that the signature should have an expiry date - // 10 minutes from now. - let expires: Timestamp = Timestamp::now_utc().checked_add(Duration::minutes(10)).unwrap(); - - // =========================================================================== - // Step 5: Holder creates and signs a verifiable presentation from the issued credential. - // =========================================================================== - - // Deserialize the credential. - let credential: Credential = Credential::from_json(credential_json.as_str())?; - - // Create an unsigned Presentation from the previously issued Verifiable Credential. - let mut presentation: Presentation = PresentationBuilder::default() - .holder(Url::parse(alice.did().as_ref())?) - .credential(credential) - .build()?; - - // Sign the verifiable presentation using the holder's verification method - // and include the requested challenge and expiry timestamp. - alice - .sign( - "#aliceKey", - &mut presentation, - ProofOptions::new().challenge(challenge.to_string()).expires(expires), - ) - .await?; - - // =========================================================================== - // Step 6: Holder sends a verifiable presentation to the verifier. - // =========================================================================== - - // Convert the Verifiable Presentation to JSON to send it to the verifier. - let presentation_json: String = presentation.to_json()?; - - // =========================================================================== - // Step 7: Verifier receives the Verifiable Presentation and verifies it. - // =========================================================================== - - // Deserialize the presentation from the holder: - let presentation: Presentation = Presentation::from_json(&presentation_json)?; - - // The verifier wants the following requirements to be satisfied: - // - Signature verification (including checking the requested challenge to mitigate replay attacks) - // - Presentation validation must fail if credentials expiring within the next 10 hours are encountered - // - The presentation holder must always be the subject, regardless of the presence of the nonTransferable property - // - The issuance date must not be in the future. - - let presentation_verifier_options: VerifierOptions = VerifierOptions::new() - .challenge(challenge.to_owned()) - .allow_expired(false); - - // Do not allow credentials that expire within the next 10 hours. - let credential_validation_options: CredentialValidationOptions = CredentialValidationOptions::default() - .earliest_expiry_date(Timestamp::now_utc().checked_add(Duration::hours(10)).unwrap()); - - let presentation_validation_options = PresentationValidationOptions::default() - .presentation_verifier_options(presentation_verifier_options.clone()) - .shared_validation_options(credential_validation_options) - .subject_holder_relationship(SubjectHolderRelationship::AlwaysSubject); - - // Validate the presentation and all the credentials included in it. - let resolver: Resolver = Resolver::new().await?; - resolver - .verify_presentation( - &presentation, - &presentation_validation_options, - FailFast::FirstError, - None, - None, - ) - .await?; - - // Since no errors were thrown by `verify_presentation` we know that the validation was successful. - println!("VP successfully validated"); - - // Note that we did not declare a latest allowed issuance date for credentials. This is because we only want to check - // that the credentials do not have an issuance date in the future which is a default check. - - Ok(()) -} diff --git a/examples_legacy/account/encryption.rs b/examples_legacy/account/encryption.rs deleted file mode 100644 index 91f6f91042..0000000000 --- a/examples_legacy/account/encryption.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange. -//! -//! cargo run --example account_encryption - -use std::path::PathBuf; - -use identity_account::account::Account; -use identity_account::account::AccountBuilder; -use identity_account::types::IdentitySetup; -use identity_account::types::MethodContent; -use identity_account::Result; -use identity_account_storage::stronghold::Stronghold; -use identity_account_storage::types::AgreementInfo; -use identity_account_storage::types::CekAlgorithm; -use identity_account_storage::types::EncryptedData; -use identity_account_storage::types::EncryptionAlgorithm; -use identity_iota::did::MethodScope; -use identity_iota_client_legacy::document::ResolvedIotaDocument; -use identity_iota_client_legacy::tangle::Resolver; -use identity_iota_core_legacy::document::IotaVerificationMethod; - -#[tokio::main] -async fn main() -> Result<()> { - // 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. - - // Sets the location and password for the Stronghold - // - // Stronghold is an encrypted file that manages private keys. - // It implements best practices for security and is the recommended way of handling private keys. - let stronghold_path: PathBuf = "./example-strong.hodl".into(); - let password: String = "my-password".to_owned(); - let stronghold: Stronghold = Stronghold::new(&stronghold_path, password, None).await?; - - // Alice creates and publishes their DID Document (see create_did and manipulate_did examples). - let mut builder: AccountBuilder = Account::builder().storage(stronghold); - let mut alice_account: Account = builder.create_identity(IdentitySetup::default()).await?; - alice_account - .update_identity() - .create_method() - .content(MethodContent::GenerateX25519) - .fragment("kex-0") - .scope(MethodScope::key_agreement()) - .apply() - .await?; - - // Bob creates and publishes their DID Document (see create_did and manipulate_did examples). - let mut bob_account: Account = builder.create_identity(IdentitySetup::default()).await?; - bob_account - .update_identity() - .create_method() - .content(MethodContent::GenerateX25519) - .fragment("kex-0") - .scope(MethodScope::key_agreement()) - .apply() - .await?; - - // 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 resolver: Resolver = Resolver::new().await?; - - // Alice: resolves Bob's DID Document and extracts their public key. - let bob_document: ResolvedIotaDocument = resolver.resolve(bob_account.did()).await?; - let bob_method: &IotaVerificationMethod = bob_document - .document - .resolve_method("kex-0", Some(MethodScope::key_agreement())) - .unwrap(); - let bob_public_key: Vec = bob_method.data().try_decode()?; - - // Alice encrypts the data using Diffie-Hellman key exchange - let agreement: AgreementInfo = AgreementInfo::new(b"Alice".to_vec(), b"Bob".to_vec(), Vec::new(), Vec::new()); - let encryption_algorithm: EncryptionAlgorithm = EncryptionAlgorithm::AES256GCM; - let cek_algorithm: CekAlgorithm = CekAlgorithm::ECDH_ES(agreement); - - let message: &[u8] = b"This msg will be encrypted and decrypted"; - let encrypted_data: EncryptedData = alice_account - .encrypt_data( - message, - b"associated_data", - &encryption_algorithm, - &cek_algorithm, - bob_public_key.into(), - ) - .await?; - - // Bob must be able to decrypt the message using the shared secret. - let decrypted_msg: Vec = bob_account - .decrypt_data(encrypted_data, &encryption_algorithm, &cek_algorithm, "kex-0") - .await?; - assert_eq!(message, &decrypted_msg); - - // Both shared secret keys computed separately by Alice and Bob matched - // and were used to exchange an encrypted message. - println!("Diffie-Hellman key exchange successful!"); - Ok(()) -} diff --git a/examples_legacy/account/lazy.rs b/examples_legacy/account/lazy.rs deleted file mode 100644 index 4e95b56815..0000000000 --- a/examples_legacy/account/lazy.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_lazy -use std::path::PathBuf; - -use identity_account::account::Account; -use identity_account::types::IdentitySetup; -use identity_account::Result; -use identity_account_storage::stronghold::Stronghold; -use identity_iota::core::Url; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_core_legacy::did::IotaDID; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // Stronghold settings - let stronghold_path: PathBuf = "./example-strong.hodl".into(); - let password: String = "my-password".to_owned(); - let stronghold: Stronghold = Stronghold::new(&stronghold_path, password, None).await?; - - // Create a new Account with auto publishing set to false. - // This means updates are not pushed to the tangle automatically. - // Rather, when we publish, multiple updates are batched together. - let mut account: Account = Account::builder() - .storage(stronghold) - .autopublish(false) - .create_identity(IdentitySetup::default()) - .await?; - - // Add a new service to the local DID document. - account - .update_identity() - .create_service() - .fragment("example-service") - .type_("LinkedDomains") - .endpoint(Url::parse("https://example.org")?) - .apply() - .await?; - - // Publish the newly created DID document, - // including the new service, to the tangle. - account.publish().await?; - - // Add another service. - account - .update_identity() - .create_service() - .fragment("another-service") - .type_("LinkedDomains") - .endpoint(Url::parse("https://example.org")?) - .apply() - .await?; - - // Delete the previously added service. - account - .update_identity() - .delete_service() - .fragment("example-service") - .apply() - .await?; - - // Publish the updates as one message to the tangle. - account.publish().await?; - - // Retrieve the DID from the newly created identity. - let iota_did: &IotaDID = account.did(); - - // Prints the Identity Resolver Explorer URL. - // The entire history can be observed on this page by clicking "Loading History". - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(iota_did)? - ); - - Ok(()) -} diff --git a/examples_legacy/account/manipulate_did.rs b/examples_legacy/account/manipulate_did.rs deleted file mode 100644 index 46f3b163db..0000000000 --- a/examples_legacy/account/manipulate_did.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_manipulate - -use std::path::PathBuf; - -use identity_account::account::Account; -use identity_account::types::IdentitySetup; -use identity_account::types::MethodContent; -use identity_account::Result; -use identity_account_storage::stronghold::Stronghold; -use identity_iota::core::Url; -use identity_iota::did::MethodRelationship; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_core_legacy::did::IotaDID; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // =========================================================================== - // Create Identity - Similar to create_did example - // =========================================================================== - - // Stronghold settings - let stronghold_path: PathBuf = "./example-strong.hodl".into(); - let password: String = "my-password".to_owned(); - let stronghold: Stronghold = Stronghold::new(&stronghold_path, password, None).await?; - - // Create a new Account with the default configuration - let mut account: Account = Account::builder() - .storage(stronghold) - .create_identity(IdentitySetup::default()) - .await?; - - // =========================================================================== - // Identity Manipulation - // =========================================================================== - - // Add another Ed25519 verification method to the identity - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("my-next-key") - .apply() - .await?; - - // Associate the newly created method with additional verification relationships - account - .update_identity() - .attach_method_relationship() - .fragment("my-next-key") - .relationships(vec![ - MethodRelationship::CapabilityDelegation, - MethodRelationship::CapabilityInvocation, - ]) - .apply() - .await?; - - // Add a new service to the identity. - account - .update_identity() - .create_service() - .fragment("my-service-1") - .type_("MyCustomService") - .endpoint(Url::parse("https://example.com")?) - .apply() - .await?; - - // Remove the Ed25519 verification method - account - .update_identity() - .delete_method() - .fragment("my-next-key") - .apply() - .await?; - - // Retrieve the DID from the newly created identity. - let iota_did: &IotaDID = account.did(); - - // Prints the Identity Resolver Explorer URL. - // The entire history can be observed on this page by clicking "Loading History". - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(iota_did)? - ); - - Ok(()) -} diff --git a/examples_legacy/account/multiple_identities.rs b/examples_legacy/account/multiple_identities.rs deleted file mode 100644 index b1d1b6e50c..0000000000 --- a/examples_legacy/account/multiple_identities.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_multiple - -use std::path::PathBuf; - -use identity_iota::account::Account; -use identity_iota::account::AccountBuilder; -use identity_iota::account::IdentitySetup; -use identity_iota::account::MethodContent; -use identity_iota::account::Result; -use identity_iota::account_storage::Stronghold; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota::iota::IotaDID; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // Sets the location and password for the Stronghold - // - // Stronghold is an encrypted file that manages private keys. - // It implements best practices for security and is the recommended way of handling private keys. - let stronghold_path: PathBuf = "./example-strong.hodl".into(); - let password: String = "my-password".to_owned(); - let stronghold: Stronghold = Stronghold::new(&stronghold_path, password, None).await?; - - // Create an AccountBuilder to make it easier to create multiple identities. - // Every account created from the builder will use the same storage - stronghold in this case. - let mut builder: AccountBuilder = Account::builder().storage(stronghold); - - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let account1: Account = builder.create_identity(IdentitySetup::default()).await?; - - // Create a second identity which uses the same storage. - let mut account2: Account = builder.create_identity(IdentitySetup::default()).await?; - - // Retrieve the did of the identity that account1 manages. - let iota_did1: IotaDID = account1.did().to_owned(); - - // Suppose we're done with account1 and drop it. - std::mem::drop(account1); - - // Now we want to modify the iota_did1 identity - how do we do that? - // We can load the identity from storage into an account using the builder. - let mut account1: Account = builder.load_identity(iota_did1).await?; - - let account1_did: IotaDID = account1.did().clone(); - - // Now we can make modifications to the identity. - // We can even do so concurrently by spawning tasks. - let task1 = tokio::spawn(async move { - account1 - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("my-key") - .apply() - .await - }); - - let task2 = tokio::spawn(async move { - account2 - .update_identity() - .create_method() - .content(MethodContent::GenerateX25519) - .fragment("my-other-key") - .apply() - .await - }); - - task1.await.expect("task1 failed to execute to completion")?; - task2.await.expect("task2 failed to execute to completion")?; - - // Prints the Identity Resolver Explorer URL. - // The entire history can be observed on this page by clicking "Loading History". - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(&account1_did)? - ); - - Ok(()) -} diff --git a/examples_legacy/account/revoke_vc.rs b/examples_legacy/account/revoke_vc.rs deleted file mode 100644 index 062129b174..0000000000 --- a/examples_legacy/account/revoke_vc.rs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! This example shows how to revoke a verifiable credential. -//! It demonstrates two methods for revocation. The first uses a revocation bitmap of type `RevocationBitmap2022`, -//! while the second method simply removes the verification method (public key) that signed the credential -//! from the DID Document of the issuer. -//! -//! Note that this example uses the "main" network, if you are writing code against the test network then most function -//! calls will need to include information about the network, since this is not automatically inferred from the -//! arguments in all cases currently. -//! -//! cargo run --example account_revoke_vc - -use identity_account::account::Account; -use identity_account::account::AccountBuilder; -use identity_account::types::IdentitySetup; -use identity_account::types::MethodContent; -use identity_account::Result; -use identity_iota::core::json; -use identity_iota::core::FromJson; -use identity_iota::core::Url; -use identity_iota::credential::Credential; -use identity_iota::credential::CredentialBuilder; -use identity_iota::credential::CredentialValidationOptions; -use identity_iota::credential::CredentialValidator; -use identity_iota::credential::FailFast; -use identity_iota::credential::RevocationBitmapStatus; -use identity_iota::credential::Status; -use identity_iota::credential::Subject; -use identity_iota::credential::ValidationError; -use identity_iota::crypto::ProofOptions; -use identity_iota::did::RevocationBitmap; -use identity_iota::did::DID; -use identity_iota_client_legacy::document::ResolvedIotaDocument; -use identity_iota_client_legacy::tangle::Resolver; - -#[tokio::main] -async fn main() -> Result<()> { - // =========================================================================== - // Create a Verifiable Credential. - // =========================================================================== - - // Create an account builder with in-memory storage for simplicity. - // See `create_did` example to configure Stronghold storage. - let mut builder: AccountBuilder = Account::builder(); - - // Create an identity for the issuer. - let mut issuer: Account = builder.create_identity(IdentitySetup::default()).await?; - - // Add a dedicated verification method to the issuer, with which to sign credentials. - issuer - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("key-1") - .apply() - .await?; - - // Create a new empty revocation bitmap. No credential is revoked yet. - let revocation_bitmap: RevocationBitmap = RevocationBitmap::new(); - - // Add the RevocationBitmap as a service endpoint to allow verifiers to check the credential status. - issuer - .update_identity() - .create_service() - .fragment("my-revocation-service") - .type_(RevocationBitmap::TYPE) - .endpoint(revocation_bitmap.to_endpoint()?) - .apply() - .await?; - - // Create a credential subject indicating the degree earned by Alice. - let subject: Subject = Subject::from_json_value(json!({ - "id": "did:iota:B8DucnzULJ9E8cmaReYoePU2b7UKE9WKxyEVov8tQA7H", - "name": "Alice", - "degree": "Bachelor of Science and Arts", - "GPA": "4.0", - }))?; - - // Create an unsigned `UniversityDegree` credential for Alice. - // The issuer also chooses a unique `RevocationBitmap` index to be able to revoke it later. - let service_url = issuer.did().to_url().join("#my-revocation-service")?; - let credential_index: u32 = 5; - let status: Status = RevocationBitmapStatus::new(service_url, credential_index).into(); - - // Build credential using subject above, status, and issuer. - let mut credential: Credential = CredentialBuilder::default() - .id(Url::parse("https://example.edu/credentials/3732")?) - .issuer(Url::parse(issuer.did().as_str())?) - .type_("UniversityDegreeCredential") - .status(status) - .subject(subject) - .build()?; - - // Sign the Credential with the issuer's verification method. - issuer.sign("#key-1", &mut credential, ProofOptions::default()).await?; - - let validation_result = CredentialValidator::validate( - &credential, - issuer.document(), - &CredentialValidationOptions::default(), - FailFast::FirstError, - ); - - // The credential wasn't revoked, so we expect the validation to succeed. - assert!(validation_result.is_ok()); - - // =========================================================================== - // Revocation of the Verifiable Credential. - // =========================================================================== - - // Update the RevocationBitmap service in the issuer's DID Document. - // This revokes the credential's unique index. - issuer - .revoke_credentials("my-revocation-service", &[credential_index]) - .await?; - - let validation_result = CredentialValidator::validate( - &credential, - issuer.document(), - &CredentialValidationOptions::default(), - FailFast::FirstError, - ); - - // We expect validation to no longer succeed because the credential was revoked. - assert!(matches!( - validation_result.unwrap_err().validation_errors[0], - ValidationError::Revoked - )); - - // =========================================================================== - // Alternative revocation of the Verifiable Credential. - // =========================================================================== - - // By removing the verification method, that signed the credential, from the issuer's DID document, - // we effectively revoke the credential, as it will no longer be possible to validate the signature. - issuer - .update_identity() - .delete_method() - .fragment("key-1") - .apply() - .await?; - - // We expect the verifiable credential to be revoked. - let resolver: Resolver = Resolver::new().await?; - let resolved_issuer_doc: ResolvedIotaDocument = resolver.resolve_credential_issuer(&credential).await?; - let validation_result = CredentialValidator::validate( - &credential, - &resolved_issuer_doc.document, - &CredentialValidationOptions::default(), - FailFast::FirstError, - ); - - println!("VC validation result: {:?}", validation_result); - assert!(validation_result.is_err()); - - println!("Credential successfully revoked!"); - - Ok(()) -} diff --git a/examples_legacy/account/signing.rs b/examples_legacy/account/signing.rs deleted file mode 100644 index 458bb109dd..0000000000 --- a/examples_legacy/account/signing.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_signing - -use std::path::PathBuf; - -use identity_account::account::Account; -use identity_account::types::IdentitySetup; -use identity_account::types::MethodContent; -use identity_account::Result; -use identity_account_storage::stronghold::Stronghold; -use identity_iota::core::json; -use identity_iota::core::FromJson; -use identity_iota::core::Url; -use identity_iota::credential::Credential; -use identity_iota::credential::Subject; -use identity_iota::crypto::KeyPair; -use identity_iota::crypto::ProofOptions; -use identity_iota::did::verifiable::VerifierOptions; -use identity_iota::did::DID; -use identity_iota::prelude::*; -use identity_iota_client_legacy::document::ResolvedIotaDocument; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_core_legacy::did::IotaDID; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // =========================================================================== - // Create Identity - Similar to create_did example - // =========================================================================== - - // Stronghold settings - let stronghold_path: PathBuf = "./example-strong.hodl".into(); - let password: String = "my-password".to_owned(); - let stronghold: Stronghold = Stronghold::new(&stronghold_path, password, None).await?; - - // Create a new Account with stronghold storage. - let mut account: Account = Account::builder() - .storage(stronghold) - .create_identity(IdentitySetup::default()) - .await?; - - // =========================================================================== - // Signing Example - // =========================================================================== - - // Add a new Ed25519 Verification Method to the identity - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("key-1") - .apply() - .await?; - - // Create a subject DID for the recipient of a `UniversityDegree` credential. - let subject_key: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let subject_did: IotaDID = IotaDID::new(subject_key.public().as_ref())?; - - // Create the actual Verifiable Credential subject. - let subject: Subject = Subject::from_json_value(json!({ - "id": subject_did.as_str(), - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts" - } - }))?; - - // Issue an unsigned Credential... - let mut credential: Credential = Credential::builder(Default::default()) - .issuer(Url::parse(account.did().as_str())?) - .type_("UniversityDegreeCredential") - .subject(subject) - .build()?; - - // ...and sign the Credential with the previously created Verification Method - account.sign("key-1", &mut credential, ProofOptions::default()).await?; - - println!("[Example] Local Credential = {:#}", credential); - - // Fetch the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - let resolved: ResolvedIotaDocument = account.resolve_identity().await?; - - // Retrieve the DID from the newly created identity. - let iota_did: &IotaDID = account.did(); - - // Prints the Identity Resolver Explorer URL. - // The entire history can be observed on this page by clicking "Loading History". - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(iota_did)? - ); - - // Ensure the resolved DID Document can verify the credential signature - let verified: bool = resolved - .document - .verify_data(&credential, &VerifierOptions::default()) - .is_ok(); - - println!("[Example] Credential Verified = {}", verified); - - Ok(()) -} diff --git a/examples_legacy/account/unchecked.rs b/examples_legacy/account/unchecked.rs deleted file mode 100644 index 8b9b704b64..0000000000 --- a/examples_legacy/account/unchecked.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! cargo run --example account_unchecked - -use std::path::PathBuf; - -use identity_account::account::Account; -use identity_account::types::IdentitySetup; -use identity_account::Result; -use identity_account_storage::stronghold::Stronghold; -use identity_iota::core::Timestamp; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::document::IotaDocument; - -#[tokio::main] -async fn main() -> Result<()> { - pretty_env_logger::init(); - - // Sets the location and password for the Stronghold - // - // Stronghold is an encrypted file that manages private keys. - // It implements best practices for security and is the recommended way of handling private keys. - let stronghold_path: PathBuf = "./example-strong.hodl".into(); - let password: String = "my-password".to_owned(); - let stronghold: Stronghold = Stronghold::new(&stronghold_path, password, None).await?; - - // Create a new identity using Stronghold as local storage. - // - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let mut account: Account = Account::builder() - .storage(stronghold) - .create_identity(IdentitySetup::default()) - .await?; - - // Get a copy of the document this account manages. - // We will apply updates to the document, and overwrite the account's current document. - let mut document: IotaDocument = account.document().clone(); - - // Add a custom property to the document. - document - .properties_mut_unchecked() - .insert("myCustomPropertyKey".into(), "value".into()); - - // Override the updated field timestamp to 24 hours (= 86400 seconds) in the future, - // because we can. This is usually set automatically by Account::update_identity. - let timestamp: Timestamp = Timestamp::from_unix(Timestamp::now_utc().to_unix() + 86400)?; - document.metadata.updated = Some(timestamp); - - // Update the identity without validation and publish the result to the Tangle - // (depending on the account's autopublish setting). - // The responsibility is on the caller to provide a valid document which the account - // can continue to use. Failing to do so can corrupt the identity; use with caution! - account.update_document_unchecked(document).await?; - - // Retrieve the did of the newly created identity. - let iota_did: &IotaDID = account.did(); - - // Print the local state of the DID Document - println!("[Example] Local Document from {} = {:#?}", iota_did, account.document()); - - // Prints the Identity Resolver Explorer URL. - // The entire history can be observed on this page by clicking "Loading History". - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(iota_did)? - ); - - Ok(()) -} diff --git a/examples_legacy/getting_started.rs b/examples_legacy/getting_started.rs deleted file mode 100644 index 84f3cad8ef..0000000000 --- a/examples_legacy/getting_started.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! Introductory example to test whether the library is set up / working properly and compiles. -//! -//! cargo run --example getting_started - -use identity_iota_client_legacy::Result; - -#[tokio::main] -async fn main() -> Result<()> { - // Print IOTA Identity header - println!(); - println!(r#" _____ ____ _______ _____ _ _ _ _ "#); - println!(r#" |_ _/ __ \__ __|/\ |_ _| | | | | (_) | "#); - println!(r#" | || | | | | | / \ | | __| | ___ _ __ | |_ _| |_ _ _ "#); - println!(r#" | || | | | | | / /\ \ | | / _` |/ _ \ '_ \| __| | __| | | |"#); - println!(r#" _| || |__| | | |/ ____ \ _| || (_| | __/ | | | |_| | |_| |_| |"#); - println!(r#" |_____\____/ |_/_/ \_\ |_____\__,_|\___|_| |_|\__|_|\__|\__, |"#); - println!(r#" __/ |"#); - println!(r#" |___/ "#); - - // Print welcome text - println!(); - println!("Welcome to IOTA Identity! The library was set up correctly and everything works!"); - println!("You can try out the other examples using: cargo run --example "); - println!("Please visit https://github.com/iotaledger/identity.rs/tree/main/examples for further info!"); - println!(); - - Ok(()) -} diff --git a/examples_legacy/low-level-api/common.rs b/examples_legacy/low-level-api/common.rs deleted file mode 100644 index 10c0d49b96..0000000000 --- a/examples_legacy/low-level-api/common.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// This file contains helper functions for the examples. - -#![allow(dead_code)] - -use identity_iota::core::json; -use identity_iota::core::FromJson; -use identity_iota::core::Timestamp; -use identity_iota::core::Url; -use identity_iota::credential::Credential; -use identity_iota::credential::CredentialBuilder; -use identity_iota::credential::Subject; -use identity_iota::did::MethodScope; -use identity_iota::did::DID; -use identity_iota_client_legacy::Receipt; -use identity_iota::iota::IotaVerificationMethod; -use identity_iota::prelude::*; - -/// Helper that takes two DID Documents (identities) for issuer and subject, and -/// creates an unsigned credential with claims about subject by issuer. -pub fn issue_degree(issuer: &IotaDocument, subject: &IotaDocument) -> Result { - // Create VC "subject" field containing subject ID and claims about it. - let subject: Subject = Subject::from_json_value(json!({ - "id": subject.id().as_str(), - "name": "Alice", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts", - }, - "GPA": "4.0", - }))?; - - // Build credential using subject above and issuer. - let credential: Credential = CredentialBuilder::default() - .id(Url::parse("https://example.edu/credentials/3732")?) - .issuer(Url::parse(issuer.id().as_str())?) - .type_("UniversityDegreeCredential") - .subject(subject) - .build()?; - - Ok(credential) -} - -/// Convenience function for adding a new `VerificationMethod` with tag #newKey to a DID document -/// and performing an integration chain update, publishing it to the Tangle. -/// -/// See "manipulate_did" for further explanation. -pub async fn add_new_key( - client: &Client, - doc: &IotaDocument, - key: &KeyPair, - receipt: &Receipt, -) -> Result<(IotaDocument, KeyPair, Receipt)> { - let mut updated_doc = doc.clone(); - - // Add #newKey to the document - let new_key: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let method: IotaVerificationMethod = - IotaVerificationMethod::new(updated_doc.id().clone(), new_key.type_(), new_key.public(), "newKey")?; - assert!(updated_doc - .insert_method(method, MethodScope::VerificationMethod) - .is_ok()); - - // Prepare the update - updated_doc.metadata.previous_message_id = *receipt.message_id(); - updated_doc.metadata.updated = Some(Timestamp::now_utc()); - updated_doc.sign_self(key.private(), updated_doc.default_signing_method()?.id().clone())?; - - // Publish the update to the Tangle - let update_receipt: Receipt = client.publish_document(&updated_doc).await?; - Ok((updated_doc, new_key, update_receipt)) -} diff --git a/examples_legacy/low-level-api/create_did.rs b/examples_legacy/low-level-api/create_did.rs deleted file mode 100644 index 20ef61313f..0000000000 --- a/examples_legacy/low-level-api/create_did.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A basic example that generates and publishes a DID Document, -//! the fundamental building block for decentralized identity. -//! -//! cargo run --example create_did - -use identity_iota::prelude::*; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_client_legacy::tangle::Receipt; -use identity_iota_client_legacy::Result; -use identity_iota_core_legacy::document::IotaDocument; - -pub async fn run() -> Result<(IotaDocument, KeyPair, Receipt)> { - // Create a client instance to send messages to the Tangle. - let client: Client = Client::new().await?; - - // Generate a new Ed25519 public/private key pair. - let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - - // Create a DID Document (an identity) from the generated key pair. - let mut document: IotaDocument = IotaDocument::new(&keypair)?; - - // Sign the DID Document with the default signing method. - document.sign_self(keypair.private(), document.default_signing_method()?.id().clone())?; - - println!("DID Document JSON > {:#}", document); - - // Publish the DID Document to the Tangle. - let receipt: Receipt = client.publish_document(&document).await?; - - println!("Publish Receipt > {:#?}", receipt); - - // Display the web explorer url that shows the published message. - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "DID Document Transaction > {}", - explorer.message_url(receipt.message_id())? - ); - println!("Explore the DID Document > {}", explorer.resolver_url(document.id())?); - - Ok((document, keypair, receipt)) -} - -#[allow(dead_code)] -#[tokio::main] -async fn main() -> Result<()> { - let _ = run().await?; - - Ok(()) -} diff --git a/examples_legacy/low-level-api/key_exchange.rs b/examples_legacy/low-level-api/key_exchange.rs deleted file mode 100644 index 2ca9f5e961..0000000000 --- a/examples_legacy/low-level-api/key_exchange.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. -//! -//! cargo run --example key_exchange - -use identity_iota::crypto::KeyType; -use identity_iota::crypto::X25519; -use identity_iota::did::MethodScope; -use identity_iota::prelude::*; -use identity_iota_client_legacy::document::ResolvedIotaDocument; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::Receipt; -use identity_iota_client_legacy::tangle::TangleResolve; -use identity_iota_client_legacy::Result; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::document::IotaVerificationMethod; - -mod create_did; - -pub async fn run() -> Result<()> { - // 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. - let client: Client = Client::new().await?; - - // Alice creates and publishes their DID Document (see create_did and manipulate_did examples). - let (alice_did, alice_x25519): (IotaDID, KeyPair) = { - // Create a DID Document. - let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let mut document: IotaDocument = IotaDocument::new(&keypair)?; - - // Insert a new X25519 KeyAgreement verification method. - let x25519: KeyPair = KeyPair::new(KeyType::X25519)?; - let method: IotaVerificationMethod = - IotaVerificationMethod::new(document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?; - document.insert_method(method, MethodScope::key_agreement())?; - - // Publish the DID Document. - document.sign_self(keypair.private(), document.default_signing_method()?.id().clone())?; - let _: Receipt = client.publish_document(&document).await?; - (document.id().clone(), x25519) - }; - - // Bob creates and publishes their DID Document (see create_did and manipulate_did examples). - let (bob_did, bob_x25519): (IotaDID, KeyPair) = { - // Create a DID Document. - let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let mut document: IotaDocument = IotaDocument::new(&keypair)?; - - // Insert a new X25519 KeyAgreement verification method. - let x25519: KeyPair = KeyPair::new(KeyType::X25519)?; - let method: IotaVerificationMethod = - IotaVerificationMethod::new(document.id().clone(), KeyType::X25519, x25519.public(), "kex-0")?; - document.insert_method(method, MethodScope::key_agreement())?; - - // Publish the DID Document. - document.sign_self(keypair.private(), document.default_signing_method()?.id().clone())?; - let _: Receipt = client.publish_document(&document).await?; - (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: ResolvedIotaDocument = client.resolve(&bob_did).await?; - let bob_method: &IotaVerificationMethod = bob_document - .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: ResolvedIotaDocument = client.resolve(&alice_did).await?; - let alice_method: &IotaVerificationMethod = alice_document - .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(()) -} - -#[tokio::main] -async fn main() -> Result<()> { - run().await?; - Ok(()) -} diff --git a/examples_legacy/low-level-api/manipulate_did.rs b/examples_legacy/low-level-api/manipulate_did.rs deleted file mode 100644 index f32a867bf8..0000000000 --- a/examples_legacy/low-level-api/manipulate_did.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! An example that demonstrates how to publish changes to the integration chain to update a -//! DID Document. -//! -//! cargo run --example update_did - -use identity_iota::core::json; -use identity_iota::core::FromJson; -use identity_iota::core::Timestamp; -use identity_iota::did::MethodScope; -use identity_iota::did::Service; -use identity_iota::did::DID; -use identity_iota::prelude::*; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_client_legacy::tangle::Receipt; -use identity_iota_client_legacy::Result; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::document::IotaService; -use identity_iota_core_legacy::document::IotaVerificationMethod; - -mod create_did; - -pub async fn run() -> Result<(IotaDocument, KeyPair, KeyPair, Receipt, Receipt)> { - // Create a client instance to send messages to the Tangle. - let client: Client = Client::new().await?; - - // Create a signed DID Document and KeyPair (see create_did.rs). - let (mut document, keypair, receipt): (IotaDocument, KeyPair, Receipt) = create_did::run().await?; - - // Add a new VerificationMethod with a new keypair - let new_key: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let method: IotaVerificationMethod = - IotaVerificationMethod::new(document.id().clone(), new_key.type_(), new_key.public(), "newKey")?; - assert!(document.insert_method(method, MethodScope::VerificationMethod).is_ok()); - - // Add a new Service - let service: IotaService = Service::from_json_value(json!({ - "id": document.id().to_url().join("#linked-domain")?, - "type": "LinkedDomains", - "serviceEndpoint": "https://iota.org" - }))?; - assert!(document.insert_service(service)); - - // Add the messageId of the previous message in the chain. - // This is REQUIRED in order for the messages to form a chain. - // Skipping / forgetting this will render the publication useless. - document.metadata.previous_message_id = *receipt.message_id(); - document.metadata.updated = Some(Timestamp::now_utc()); - - // Sign the DID Document with the original private key. - document.sign_self(keypair.private(), document.default_signing_method()?.id().clone())?; - - // Publish the updated DID Document to the Tangle. - let update_receipt: Receipt = client.publish_document(&document).await?; - - println!("Publish Receipt > {:#?}", update_receipt); - - // Display the web explorer url that shows the published message. - let explorer: &ExplorerUrl = ExplorerUrl::mainnet(); - println!( - "DID Document Transaction > {}", - explorer.message_url(update_receipt.message_id())? - ); - println!("Explore the DID Document > {}", explorer.resolver_url(document.id())?); - - Ok((document, keypair, new_key, receipt, update_receipt)) -} - -#[tokio::main] -async fn main() -> Result<()> { - let _ = run().await?; - Ok(()) -} diff --git a/examples_legacy/low-level-api/private_tangle.rs b/examples_legacy/low-level-api/private_tangle.rs deleted file mode 100644 index 0edfee5f5a..0000000000 --- a/examples_legacy/low-level-api/private_tangle.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A basic example that generates and publishes a DID Document -//! to a private tangle. -//! It can be run together with a local hornet node. -//! Refer to https://github.com/iotaledger/one-click-tangle/tree/chrysalis/hornet-private-net -//! for setup instructions. -//! -//! cargo run --example private_tangle - -use identity_iota::crypto::KeyType; -use identity_iota::prelude::*; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::ClientBuilder; -use identity_iota_client_legacy::tangle::DIDMessageEncoding; -use identity_iota_client_legacy::tangle::ExplorerUrl; -use identity_iota_client_legacy::tangle::Receipt; -use identity_iota_client_legacy::Result; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::tangle::Network; - -#[tokio::main] -pub async fn main() -> Result<()> { - // Set-up for private Tangle - // You can use https://github.com/iotaledger/one-click-tangle for a local setup. - // The `network_name` needs to match the id of the network or a part of it. - // As an example we are treating the devnet as a private tangle, so we use `dev`. - // When running the local setup, we can use `tangle` since the id of the one-click - // private tangle is `private-tangle`, but we can only use 6 characters. - // Keep in mind, there are easier ways to change to devnet via `Network::Devnet` - let network_name = "dev"; - let network = Network::try_from_name(network_name)?; - - // If you deployed an explorer locally this would usually be `http://127.0.0.1:8082` - let explorer = ExplorerUrl::parse("https://explorer.iota.org/devnet")?; - - // In a locally running one-click tangle, this would usually be `http://127.0.0.1:14265` - let private_node_url = "https://api.lb-0.h.chrysalis-devnet.iota.cafe"; - - // Use DIDMessageEncoding::Json instead to publish plaintext messages to the Tangle for debugging. - let encoding = DIDMessageEncoding::JsonBrotli; - - let client: Client = ClientBuilder::new() - .network(network.clone()) - .encoding(encoding) - .primary_node(private_node_url, None, None)? - .build() - .await?; - - // Generate a new Ed25519 public/private key pair. - let keypair: KeyPair = KeyPair::new(KeyType::Ed25519)?; - - // Create a DID with the network set explicitly. - let mut document: IotaDocument = IotaDocument::new_with_options(&keypair, Some(client.network().name()), None)?; - - // Sign the DID Document with the default signing method. - document.sign_self(keypair.private(), document.default_signing_method()?.id().clone())?; - - // Publish the DID Document to the Tangle. - let receipt: Receipt = match client.publish_document(&document).await { - Ok(receipt) => receipt, - Err(err) => { - eprintln!("Error > {:?}", err); - eprintln!("Is your private Tangle node listening on {}?", private_node_url); - return Ok(()); - } - }; - - println!("Publish Receipt > {:#?}", receipt); - - // Prints the Identity Resolver Explorer URL, the entire history can be observed on this page by "Loading History". - println!( - "[Example] Explore the DID Document = {}", - explorer.resolver_url(document.id())? - ); - - Ok(()) -} diff --git a/examples_legacy/low-level-api/resolve_did.rs b/examples_legacy/low-level-api/resolve_did.rs deleted file mode 100644 index 8564753e4d..0000000000 --- a/examples_legacy/low-level-api/resolve_did.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! A basic example that generates a DID Document, publishes it to the Tangle, -//! and retrieves information through DID Document resolution/dereferencing. -//! -//! See also https://www.w3.org/TR/did-core/#did-resolution and https://www.w3.org/TR/did-core/#did-url-dereferencing -//! -//! cargo run --example resolve_did - -use identity_iota::prelude::*; -use identity_iota_client_legacy::document::ResolvedIotaDocument; -use identity_iota_client_legacy::tangle::Receipt; -use identity_iota_client_legacy::tangle::Resolver; -use identity_iota_client_legacy::Result; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::document::IotaDocument; - -mod create_did; - -#[tokio::main] -async fn main() -> Result<()> { - // Create a signed DID Document and KeyPair (see create_did.rs). - let (document, _, _): (IotaDocument, KeyPair, Receipt) = create_did::run().await?; - - // =========================================================================== - // Resolve DID Document - // =========================================================================== - - let doc_did: &IotaDID = document.id(); - - // Retrieve the published DID Document from the Tangle. - let resolver: Resolver = Resolver::new().await?; - let resolved_did_document: ResolvedIotaDocument = resolver.resolve(doc_did).await?; - - println!("Resolved DID Document > {:#?}", resolved_did_document); - - // The resolved document should be the same as what we published. - assert_eq!(resolved_did_document.document, document); - - Ok(()) -} diff --git a/examples_legacy/low-level-api/resolve_history.rs b/examples_legacy/low-level-api/resolve_history.rs deleted file mode 100644 index 231a5a6912..0000000000 --- a/examples_legacy/low-level-api/resolve_history.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! Advanced example that performs multiple updates and demonstrates how to resolve the -//! DID Document history to view them. -//! -//! cargo run --example did_history - -use identity_iota::core::json; -use identity_iota::core::FromJson; -use identity_iota::core::Timestamp; -use identity_iota::crypto::KeyPair; -use identity_iota::did::MethodScope; -use identity_iota::did::Service; -use identity_iota::did::DID; -use identity_iota::prelude::*; -use identity_iota_client_legacy::chain::DocumentHistory; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::Receipt; -use identity_iota_client_legacy::Result; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::document::IotaService; -use identity_iota_core_legacy::document::IotaVerificationMethod; - -mod create_did; - -#[rustfmt::skip] -#[tokio::main] -async fn main() -> Result<()> { - // Create a client instance to send messages to the Tangle. - let client: Client = Client::new().await?; - - // =========================================================================== - // DID Creation - // =========================================================================== - - // Create a signed DID Document and KeyPair (see "create_did.rs" example). - let (document, keypair, original_receipt): (IotaDocument, KeyPair, Receipt) = create_did::run().await?; - - // =========================================================================== - // Integration Chain Spam - // =========================================================================== - - // Publish several spam messages to the same index as the integration chain on the Tangle. - // These are not valid DID messages and are simply to demonstrate that invalid messages - // can be included in the history for debugging invalid DID documents. - let int_index: &str = document.integration_index(); - client.publish_json(int_index, &json!({ "intSpam:1": true })).await?; - client.publish_json(int_index, &json!({ "intSpam:2": true })).await?; - client.publish_json(int_index, &json!({ "intSpam:3": true })).await?; - client.publish_json(int_index, &json!({ "intSpam:4": true })).await?; - client.publish_json(int_index, &json!({ "intSpam:5": true })).await?; - - // =========================================================================== - // Integration Chain Update 1 - // =========================================================================== - - // Prepare an integration chain update, which writes the full updated DID document to the Tangle. - let int_doc_1 = { - let mut int_doc_1 = document.clone(); - - // Add a new Service with the tag "linked-domain-1". - let service: IotaService = Service::from_json_value(json!({ - "id": int_doc_1.id().to_url().join("#linked-domain-1")?, - "type": "LinkedDomains", - "serviceEndpoint": "https://iota.org/" - }))?; - assert!(int_doc_1.insert_service(service)); - - // Add a second Service with the tag "linked-domain-2". - let service: IotaService = Service::from_json_value(json!({ - "id": int_doc_1.id().to_url().join("#linked-domain-2")?, - "type": "LinkedDomains", - "serviceEndpoint": { - "origins": ["https://iota.org/", "https://example.com/"] - } - }))?; - assert!(int_doc_1.insert_service(service)); - - // Add a new VerificationMethod with a new KeyPair, with the tag "keys-1" - let keys_1: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let method_1: IotaVerificationMethod = IotaVerificationMethod::new(int_doc_1.id().clone(), keys_1.type_(), keys_1.public(), "keys-1")?; - assert!(int_doc_1.insert_method(method_1, MethodScope::VerificationMethod).is_ok()); - - // Add the `message_id` of the previous message in the chain. - // This is REQUIRED in order for the messages to form a chain. - // Skipping / forgetting this will render the publication useless. - int_doc_1.metadata.previous_message_id = *original_receipt.message_id(); - int_doc_1.metadata.updated = Some(Timestamp::now_utc()); - - // Sign the DID Document with the original private key. - int_doc_1.sign_self(keypair.private(), int_doc_1.default_signing_method()?.id().clone())?; - - int_doc_1 - }; - - // Publish the updated DID Document to the Tangle, updating the integration chain. - // This may take a few seconds to complete proof-of-work. - let int_receipt_1: Receipt = client.publish_document(&int_doc_1).await?; - - // =========================================================================== - // DID History 1 - // =========================================================================== - - // Retrieve the message history of the DID. - let history_1: DocumentHistory = client.resolve_history(document.id()).await?; - - // The history shows two documents in the integration chain. - println!("History (1) = {:#?}", history_1); - - // =========================================================================== - // Integration Chain Update 2 - // =========================================================================== - - // Publish a second integration chain update - let int_doc_2 = { - let mut int_doc_2 = int_doc_1.clone(); - - // Remove the #keys-1 VerificationMethod - int_doc_2.remove_method(&int_doc_2.id().to_url().join("#keys-1")?)?; - - // Remove the #linked-domain-1 Service - int_doc_2.remove_service(&int_doc_2.id().to_url().join("#linked-domain-1")?); - - // Add a VerificationMethod with a new KeyPair, called "keys-2" - let keys_2: KeyPair = KeyPair::new(KeyType::Ed25519)?; - let method_2: IotaVerificationMethod = IotaVerificationMethod::new(int_doc_2.id().clone(), keys_2.type_(), keys_2.public(), "keys-2")?; - assert!(int_doc_2.insert_method(method_2, MethodScope::VerificationMethod).is_ok()); - - // Note: the `previous_message_id` points to the `message_id` of the last integration chain - // update. - int_doc_2.metadata.previous_message_id = *int_receipt_1.message_id(); - int_doc_2.metadata.updated = Some(Timestamp::now_utc()); - - int_doc_2.sign_self(keypair.private(), int_doc_2.default_signing_method()?.id().clone())?; - int_doc_2 - }; - let _int_receipt_2: Receipt = client.publish_document(&int_doc_2).await?; - - // =========================================================================== - // DID History 2 - // =========================================================================== - - // Retrieve the updated message history of the DID. - let history_2: DocumentHistory = client.resolve_history(document.id()).await?; - - // The history now shows three documents in the integration chain. - println!("History (2) = {:#?}", history_2); - - Ok(()) -} diff --git a/identity_account/Cargo.toml b/identity_account/Cargo.toml deleted file mode 100644 index 47363371fa..0000000000 --- a/identity_account/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "identity_account" -version = "0.7.0-alpha.1" -authors = ["IOTA Stiftung"] -edition = "2021" -homepage = "https://www.iota.org" -keywords = ["iota", "tangle", "identity", "did"] -license = "Apache-2.0" -publish = false -readme = "./README.md" -repository = "https://github.com/iotaledger/identity.rs" -description = "High-level interface for managing IOTA DID Documents." - -[workspace] - -[dependencies] -identity_account_storage = { version = "0.7.0-alpha.1", path = "../identity_account_storage", default-features = false } -identity_core = { version = "0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_credential = { version = "0.7.0-alpha.1", path = "../identity_credential", default-features = false } -identity_did = { version = "0.7.0-alpha.1", path = "../identity_did", default-features = false } -identity_iota_client_legacy = { version = "0.7.0-alpha.1", path = "../identity_iota_client_legacy", default-features = false } -identity_iota_core_legacy = { version = "0.7.0-alpha.1", path = "../identity_iota_core_legacy", default-features = false } -log = { version = "0.4", default-features = false } -paste = { version = "1.0" } -rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } -serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } -strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } -thiserror = { version = "1.0" } - -[dev-dependencies] -futures = { version = "0.3" } -rusty-fork = { version = "0.3" } -tokio = { version = "1.17.0", default-features = false, features = ["macros", "rt", "rt-multi-thread", "sync"] } - -[features] -default = ["stronghold", "send-sync-storage", "encryption", "revocation-bitmap"] -mem-client = [] -stronghold = ["identity_account_storage/stronghold"] -send-sync-storage = ["identity_account_storage/send-sync-storage"] -# Enables encryption and decryption functionality. -encryption = ["identity_account_storage/encryption"] - -# Enables revocation with `RevocationBitmap2022`. -revocation-bitmap = ["identity_iota_client_legacy/revocation-bitmap"] diff --git a/identity_account/README.md b/identity_account/README.md deleted file mode 100644 index aa8e08546c..0000000000 --- a/identity_account/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# IOTA Identity - Account - -The [`Account`](crate::account::Account) is an interface for creating and managing identities on the IOTA Tangle, handling publishing and secure storage automatically. It provides convenience functions for: - -- Creating and publishing a new IOTA DID. -- Updating DID Document contents: - - Verification Methods. - - Verification Relationships. - - Services. -- Managing private cryptographic keys securely. -- Signing credentials. -- Encrypting messages. - -## Account Creation - -Creating an [`Account`](crate::account::Account) is done through the [`AccountBuilder`](crate::account::AccountBuilder). - -```rust,ignore -let account: Account = Account::builder() - .create_identity(IdentitySetup::default()) - .await?; -``` - - -## Update Operations - -Updating a DID Document can be performed through the [`update_identity`](crate::account::Account::update_identity) function on the [`Account`](crate::account::Account). For example, adding a new verification method to the DID Document: - -```rust,ignore -account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("my-next-key") - .apply() - .await?; -``` - -The above code generates a new Ed25519 keypair, writes it to [`Storage`](identity_account_storage::storage::Storage), embeds it in a new verification method, and publishes the updated DID Document to the Tangle. - -See the [`IdentityUpdater`](crate::types::IdentityUpdater) for a list of provided update operations. - diff --git a/identity_account/src/account/account.rs b/identity_account/src/account/account.rs deleted file mode 100644 index 88401802e2..0000000000 --- a/identity_account/src/account/account.rs +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt::Debug; -use std::ops::Deref; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -use identity_account_storage::crypto::RemoteEd25519; -use identity_account_storage::crypto::RemoteKey; -use identity_account_storage::identity::ChainState; -use identity_account_storage::storage::Storage; -use identity_account_storage::types::KeyLocation; -use identity_core::convert::FromJson; -use identity_core::convert::ToJson; -use identity_core::crypto::KeyType; -use identity_core::crypto::ProofOptions; -use identity_core::crypto::SetSignature; -use identity_iota_client_legacy::chain::DocumentChain; -use identity_iota_client_legacy::document::ResolvedIotaDocument; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::PublishType; -use identity_iota_client_legacy::tangle::SharedPtr; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::did::IotaDIDUrl; -use identity_iota_core_legacy::diff::DiffMessage; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::document::IotaVerificationMethod; -use identity_iota_core_legacy::tangle::MessageId; -use identity_iota_core_legacy::tangle::MessageIdExt; -use serde::Serialize; - -use crate::account::AccountBuilder; -use crate::account::PublishOptions; -use crate::types::IdentitySetup; -use crate::types::IdentityState; -use crate::types::IdentityUpdater; -use crate::updates::create_identity; -use crate::updates::Update; -use crate::Error; -use crate::Result; - -use super::config::AccountSetup; -use super::config::AutoSave; -use super::AccountConfig; - -/// An account manages one identity. -/// -/// It handles private keys, writing to storage and -/// publishing to the Tangle. -#[derive(Debug)] -pub struct Account> -where - C: SharedPtr, -{ - config: AccountConfig, - storage: Arc, - client: C, - actions: AtomicUsize, - chain_state: ChainState, - document: IotaDocument, -} - -impl Account -where - C: SharedPtr, -{ - // =========================================================================== - // Constructors - // =========================================================================== - - /// Creates a new [AccountBuilder]. - pub fn builder() -> AccountBuilder { - AccountBuilder::new() - } - - /// Creates a new `Account` instance with the given `config`. - async fn with_setup(setup: AccountSetup, chain_state: ChainState, document: IotaDocument) -> Result { - Ok(Self { - config: setup.config, - storage: setup.storage, - client: setup.client, - actions: AtomicUsize::new(0), - chain_state, - document, - }) - } - - /// Creates a new identity and returns an [`Account`] instance to manage it. - /// - /// The identity is stored locally in the [`Storage`] given in [`AccountSetup`]. The DID network - /// is automatically determined by the [`Client`] used to publish it. - /// - /// See [`IdentitySetup`] to customize the identity creation. - pub(crate) async fn create_identity(account_setup: AccountSetup, identity_setup: IdentitySetup) -> Result { - let document: IotaDocument = create_identity( - identity_setup, - account_setup.client.deref().network().name(), - account_setup.storage.deref(), - ) - .await?; - - let mut account = Self::with_setup(account_setup, ChainState::new(), document).await?; - - account.store_state().await?; - - account.publish_internal(false, PublishOptions::default()).await?; - - Ok(account) - } - - /// Creates an [`Account`] for an existing identity, if it exists in the [`Storage`]. - /// - /// # Warning - /// - /// Callers are expected **not** to load the same [`IotaDID`] into more than one account, - /// as that would cause race conditions when updating the identity. - pub(crate) async fn load_identity(setup: AccountSetup, did: IotaDID) -> Result { - // Ensure the DID matches the client network. - if did.network_str() != setup.client.network().name_str() { - return Err(Error::IotaClientError( - identity_iota_client_legacy::Error::IncompatibleNetwork(format!( - "DID network {} does not match account network {}", - did.network_str(), - setup.client.network().name_str() - )), - )); - } - - // Ensure the identity exists in storage - let identity_state_bytes: Vec = setup - .storage - .blob_get(did.as_ref()) - .await? - .ok_or(Error::IdentityNotFound)?; - let identity_state: IdentityState = IdentityState::from_json_slice(&identity_state_bytes)?; - let chain_state: ChainState = identity_state - .chain_state()? - .ok_or_else(|| Error::InvalidIdentityState("missing chain state".to_owned()))?; - let document: IotaDocument = identity_state - .document()? - .ok_or_else(|| Error::InvalidIdentityState("missing document".to_owned()))?; - - Self::with_setup(setup, chain_state, document).await - } - - // =========================================================================== - // Getters & Setters - // =========================================================================== - - /// Returns a reference counter to the [Storage] implementation. - pub fn storage(&self) -> &Arc { - &self.storage - } - - /// Returns whether auto-publish is enabled. - pub fn autopublish(&self) -> bool { - self.config.autopublish - } - - /// Returns the auto-save configuration value. - pub fn autosave(&self) -> AutoSave { - self.config.autosave - } - - /// Returns the total number of actions executed by this instance. - pub fn actions(&self) -> usize { - self.actions.load(Ordering::SeqCst) - } - - /// Increments the total number of actions executed by this instance. - fn increment_actions(&self) { - self.actions.fetch_add(1, Ordering::SeqCst); - } - - /// Returns the did of the managed identity. - pub fn did(&self) -> &IotaDID { - self.document().id() - } - - /// Return the chain state of the identity. - pub fn chain_state(&self) -> &ChainState { - &self.chain_state - } - - /// Returns the DID document of the identity, which this account manages, - /// with all updates applied. - /// - /// Note: the returned document only has a valid signature after publishing an integration chain update. - /// In general, for use cases where the signature is required, it is advisable to resolve the - /// document from the Tangle. - pub fn document(&self) -> &IotaDocument { - &self.document - } - - /// Sets the [`ChainState`] for the identity this account manages, **without doing any validation**. - /// - /// # WARNING - /// - /// This method is dangerous and can easily corrupt the internal state, - /// potentially making the identity unusable. Only call this if you fully - /// understand the implications! - pub fn set_chain_state_unchecked(&mut self, chain_state: ChainState) { - self.chain_state = chain_state - } - - // =========================================================================== - // Identity - // =========================================================================== - - /// Resolves the DID Document associated with this `Account` from the Tangle. - pub async fn resolve_identity(&self) -> Result { - self.client.read_document(self.did()).await.map_err(Into::into) - } - - /// Returns the [`IdentityUpdater`] for this identity. - /// - /// On this type, various operations can be executed - /// that modify an identity, such as creating services or methods. - pub fn update_identity(&mut self) -> IdentityUpdater<'_, C> { - IdentityUpdater::new(self) - } - - /// Overwrites the [`IotaDocument`] this account manages, **without doing any validation**. - /// - /// # WARNING - /// - /// This method is dangerous and can easily corrupt the internal state, - /// potentially making the identity unusable. Only call this if you fully - /// understand the implications! - pub async fn update_document_unchecked(&mut self, document: IotaDocument) -> Result<()> { - self.document = document; - - self.increment_actions(); - - self.publish_internal(false, PublishOptions::default()).await?; - - Ok(()) - } - - /// Removes the identity from the local storage entirely. - /// - /// Note: This will remove all associated document updates and key material - recovery is NOT POSSIBLE! - pub async fn delete_identity(self) -> Result<()> { - // Remove all associated keys and events - self.storage().did_purge(self.did().as_ref()).await?; - - // Write the changes to disk - self.save(false).await?; - - Ok(()) - } - - /// Signs `data` with the key specified by `fragment`. - pub async fn sign(&self, fragment: &str, data: &mut U, options: ProofOptions) -> Result<()> - where - U: Serialize + SetSignature, - { - let method: &IotaVerificationMethod = self - .document() - .resolve_method(fragment, None) - .ok_or(Error::DIDError(identity_did::Error::MethodNotFound))?; - - self - .remote_sign_data(self.document().id(), method, data, options) - .await?; - - Ok(()) - } - - /// Push all unpublished changes to the tangle in a single message. - pub async fn publish(&mut self) -> Result<()> { - self.publish_internal(true, PublishOptions::default()).await?; - - Ok(()) - } - - /// Push all unpublished changes to the Tangle in a single message, optionally choosing - /// the signing key used or forcing an integration chain update. - /// - /// See [`PublishOptions`]. - pub async fn publish_with_options(&mut self, options: PublishOptions) -> Result<()> { - self.publish_internal(true, options).await?; - - Ok(()) - } - - /// Fetches the latest document from the tangle and **overwrites** the local document. - /// - /// If a DID is managed from distributed accounts, this should be called before making changes - /// to the identity, to avoid publishing updates that would be ignored. - pub async fn fetch_document(&mut self) -> Result<()> { - let iota_did: &IotaDID = self.did(); - let mut document_chain: DocumentChain = self.client.read_document_chain(iota_did).await?; - - // Checks if the local document is up to date - if document_chain.integration_message_id() == self.chain_state.last_integration_message_id() - && (document_chain.diff().is_empty() - || document_chain.diff_message_id() == self.chain_state.last_diff_message_id()) - { - return Ok(()); - } - - // Overwrite the current state with the most recent document - self - .chain_state - .set_last_integration_message_id(*document_chain.integration_message_id()); - self - .chain_state - .set_last_diff_message_id(*document_chain.diff_message_id()); - - std::mem::swap(&mut self.document, &mut document_chain.current_mut().document); - - self.increment_actions(); - self.store_state().await?; - Ok(()) - } - - // =========================================================================== - // Misc. Private - // =========================================================================== - - pub(crate) async fn load_document(&self) -> Result { - // TODO: An account always holds a valid identity, - // so if None is returned, that's a broken invariant. - // This should be mapped to a fatal error in the future. - let identity_state_bytes: Vec = self - .storage() - .deref() - .blob_get(self.did().as_ref()) - .await? - .ok_or(Error::IdentityNotFound)?; - let identity_state: IdentityState = IdentityState::from_json_slice(&identity_state_bytes)?; - - identity_state - .document()? - .ok_or_else(|| Error::InvalidIdentityState("document not found".to_owned())) - } - - pub(crate) async fn process_update(&mut self, update: Update) -> Result<()> { - let did = self.did().to_owned(); - update.process(&did, &mut self.document, self.storage.deref()).await?; - - self.increment_actions(); - - self.publish_internal(false, PublishOptions::default()).await?; - - Ok(()) - } - - async fn sign_self( - &self, - old_doc: &IotaDocument, - new_doc: &IotaDocument, - signing_method_query: &Option, - document: &mut IotaDocument, - ) -> Result<()> { - let signing_doc: &IotaDocument = if self.chain_state().is_new_identity() { - new_doc - } else { - old_doc - }; - - let signing_method: &IotaVerificationMethod = match signing_method_query { - Some(fragment) => signing_doc.resolve_signing_method(fragment)?, - None => signing_doc.default_signing_method()?, - }; - - self - .remote_sign_data(signing_doc.id(), signing_method, document, ProofOptions::default()) - .await?; - - Ok(()) - } - - /// Publishes according to the autopublish configuration. - async fn publish_internal(&mut self, force: bool, options: PublishOptions) -> Result<()> { - if !force && !self.config.autopublish { - return Ok(()); - } - - if self.chain_state().is_new_identity() { - // New identity - self.publish_integration_change(None, &options.sign_with).await?; - } else { - // Existing identity - let old_doc: IotaDocument = self.load_document().await?; - let new_doc: &IotaDocument = self.document(); - - // NOTE: always publish an integration update (if needed); diff chain slated for removal. - let publish_type: Option = if options.force_integration_update { - Some(PublishType::Integration) - } else if let Some(publish_type) = PublishType::new(&old_doc, new_doc) { - if self.config.testmode { - // Allow tests to pass as normal. - Some(publish_type) - } else { - Some(PublishType::Integration) - } - } else { - None - }; - - match publish_type { - Some(PublishType::Integration) => { - self - .publish_integration_change(Some(&old_doc), &options.sign_with) - .await?; - } - Some(PublishType::Diff) => { - self.publish_diff_change(&old_doc, &options.sign_with).await?; - } - None => { - // Can return early, as there is nothing new to publish or store. - return Ok(()); - } - } - } - - self.store_state().await?; - - Ok(()) - } - - async fn store_state(&self) -> Result<()> { - let identity_state: IdentityState = IdentityState::new(Some(&self.document), Some(&self.chain_state))?; - self - .storage - .blob_set(self.did().as_ref(), identity_state.to_json_vec()?) - .await?; - - self.save(false).await?; - - Ok(()) - } - - async fn publish_integration_change( - &mut self, - old_doc: Option<&IotaDocument>, - signing_method_query: &Option, - ) -> Result<()> { - log::debug!("[publish_integration_change] publishing {:?}", self.document().id()); - - let new_doc_ref: &IotaDocument = self.document(); - let mut new_doc: IotaDocument = new_doc_ref.to_owned(); - - new_doc.metadata.previous_message_id = *self.chain_state().last_integration_message_id(); - - self - .sign_self( - old_doc.unwrap_or(new_doc_ref), - new_doc_ref, - signing_method_query, - &mut new_doc, - ) - .await?; - - log::debug!( - "[publish_integration_change] publishing on index {}", - new_doc.integration_index() - ); - - let message_id: MessageId = if self.config.testmode { - // Fake publishing by returning a random message id. - let bytes: [u8; 32] = rand::random(); - MessageId::new(bytes) - } else { - self.client.publish_document(&new_doc).await?.into() - }; - - self.chain_state.set_last_integration_message_id(message_id); - // Overwrite the internal document with the published one, that has a signature. - self.document = new_doc; - - Ok(()) - } - - async fn publish_diff_change(&mut self, old_doc: &IotaDocument, signing_method_query: &Option) -> Result<()> { - log::debug!("[publish_diff_change] publishing {:?}", self.document().id()); - - let new_doc: &IotaDocument = &self.document; - - let mut previous_message_id: &MessageId = self.chain_state().last_diff_message_id(); - - // If there was no previous diff message, use the previous int message. - if previous_message_id.is_null() { - if !self.chain_state.last_integration_message_id().is_null() { - previous_message_id = self.chain_state.last_integration_message_id(); - } else { - // TODO: Return a fatal error about the invalid chain state. - } - } - - let mut diff: DiffMessage = DiffMessage::new(old_doc, new_doc, *previous_message_id)?; - - let signing_method: &IotaVerificationMethod = match signing_method_query { - Some(fragment) => old_doc.resolve_signing_method(fragment)?, - None => old_doc.default_signing_method()?, - }; - - self - .remote_sign_data(old_doc.id(), signing_method, &mut diff, ProofOptions::default()) - .await?; - - log::debug!( - "[publish_diff_change] publishing on index {}", - IotaDocument::diff_index(self.chain_state().last_integration_message_id())? - ); - - let message_id: MessageId = if self.config.testmode { - // Fake publishing by returning a random message id. - let bytes: [u8; 32] = rand::random(); - MessageId::new(bytes) - } else { - self - .client - .publish_diff(self.chain_state().last_integration_message_id(), &diff) - .await? - .into() - }; - - self.chain_state.set_last_diff_message_id(message_id); - - Ok(()) - } - - async fn save(&self, force: bool) -> Result<()> { - match self.config.autosave { - AutoSave::Every => { - self.storage().deref().flush_changes().await?; - } - AutoSave::Batch(step) if force || (step != 0 && self.actions() % step == 0) => { - self.storage().deref().flush_changes().await?; - } - AutoSave::Batch(_) | AutoSave::Never => {} - } - - Ok(()) - } - - // Helper function for remote signing. - pub(crate) async fn remote_sign_data( - &self, - did: &IotaDID, - method: &IotaVerificationMethod, - data: &mut D, - options: ProofOptions, - ) -> crate::Result<()> - where - D: Serialize + SetSignature, - { - let location: KeyLocation = KeyLocation::from_verification_method(method)?; - - // Create a private key suitable for identity_core::crypto - let private: RemoteKey<'_> = RemoteKey::new(did.as_ref(), &location, self.storage().deref()); - - let method_url: IotaDIDUrl = method.id().to_owned(); - - match location.key_type { - KeyType::Ed25519 => { - RemoteEd25519::create_signature(data, method_url.to_string(), &private, options).await?; - } - KeyType::X25519 => return Err(identity_did::Error::InvalidMethodType.into()), - } - - Ok(()) - } -} - -#[cfg(feature = "revocation-bitmap")] -mod account_revocation { - use super::Account; - use crate::account::PublishOptions; - use crate::Result; - use identity_did::did::DID; - use identity_iota_client_legacy::tangle::Client; - use identity_iota_client_legacy::tangle::SharedPtr; - use identity_iota_core_legacy::did::IotaDIDUrl; - - impl Account - where - C: SharedPtr, - { - /// If the document has a [`RevocationBitmap`][identity_did::revocation::RevocationBitmap] service identified by - /// `fragment`, revoke all specified `indices`. - pub async fn revoke_credentials(&mut self, fragment: &str, indices: &[u32]) -> Result<()> { - // Find the service to be updated. - let mut service_id: IotaDIDUrl = self.did().to_url(); - service_id.set_fragment(Some(fragment))?; - - self.document.revoke_credentials(&service_id, indices)?; - - self.increment_actions(); - self.publish_internal(false, PublishOptions::default()).await?; - Ok(()) - } - - /// If the document has a [`RevocationBitmap`][identity_did::revocation::RevocationBitmap] service identified by - /// `fragment`, unrevoke all specified `indices`. - pub async fn unrevoke_credentials(&mut self, fragment: &str, indices: &[u32]) -> Result<()> { - // Find the service to be updated. - let mut service_id: IotaDIDUrl = self.did().to_url(); - service_id.set_fragment(Some(fragment))?; - - self.document.unrevoke_credentials(&service_id, indices)?; - - self.increment_actions(); - self.publish_internal(false, PublishOptions::default()).await?; - Ok(()) - } - } -} - -#[cfg(feature = "encryption")] -mod account_encryption { - use super::Account; - use crate::Error; - use crate::Result; - use identity_account_storage::types::CekAlgorithm; - use identity_account_storage::types::EncryptedData; - use identity_account_storage::types::EncryptionAlgorithm; - use identity_account_storage::types::KeyLocation; - use identity_core::crypto::PublicKey; - use identity_iota_client_legacy::tangle::Client; - use identity_iota_client_legacy::tangle::SharedPtr; - use identity_iota_core_legacy::document::IotaVerificationMethod; - - impl Account - where - C: SharedPtr, - { - /// Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`. - /// - /// Returns an [`EncryptedData`] instance. - pub async fn encrypt_data( - &self, - plaintext: &[u8], - associated_data: &[u8], - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - public_key: PublicKey, - ) -> Result { - self - .storage() - .data_encrypt( - self.did().as_ref(), - plaintext.to_vec(), - associated_data.to_vec(), - encryption_algorithm, - cek_algorithm, - public_key, - ) - .await - .map_err(Into::into) - } - - /// Decrypts the given `data` with the key identified by `fragment` using the given `encryption_algorithm` and - /// `cek_algorithm`. - /// - /// Returns the decrypted text. - pub async fn decrypt_data( - &self, - data: EncryptedData, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - fragment: &str, - ) -> Result> { - let method: &IotaVerificationMethod = self - .document() - .resolve_method(fragment, None) - .ok_or(Error::DIDError(identity_did::Error::MethodNotFound))?; - let private_key: KeyLocation = KeyLocation::from_verification_method(method)?; - self - .storage() - .data_decrypt( - self.did().as_ref(), - data, - encryption_algorithm, - cek_algorithm, - &private_key, - ) - .await - .map_err(Into::into) - } - } -} diff --git a/identity_account/src/account/builder.rs b/identity_account/src/account/builder.rs deleted file mode 100644 index f85c34c6e3..0000000000 --- a/identity_account/src/account/builder.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::sync::Arc; - -use identity_account_storage::storage::MemStore; -use identity_account_storage::storage::Storage; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::ClientBuilder; -use identity_iota_client_legacy::tangle::SharedPtr; -use identity_iota_core_legacy::did::IotaDID; - -use crate::account::Account; -use crate::error::Result; -use crate::types::IdentitySetup; - -use super::config::AccountConfig; -use super::config::AccountSetup; -use super::config::AutoSave; - -/// An [`Account`] builder for easy account configuration. -/// -/// To reduce memory usage, accounts created from the same builder share the same [`Storage`] -/// used to store identities, and the same [`Client`] used to publish identities to the Tangle. -/// -/// The configuration on the other hand is cloned, and therefore unique for each built account. -/// This means a builder can be reconfigured in-between account creations, without affecting -/// the configuration of previously built accounts. -#[derive(Debug)] -pub struct AccountBuilder> -where - C: SharedPtr, -{ - config: AccountConfig, - storage: Option>, - client_builder: Option, - client: Option, -} - -impl AccountBuilder -where - C: SharedPtr, -{ - /// Creates a new `AccountBuilder`. - pub fn new() -> Self { - Self { - config: AccountConfig::new(), - storage: None, - client_builder: None, - client: None, - } - } - - /// Sets the account auto-save behaviour. - #[must_use] - pub fn autosave(mut self, value: AutoSave) -> Self { - self.config = self.config.autosave(value); - self - } - - /// Sets the account auto-publish behaviour. - #[must_use] - pub fn autopublish(mut self, value: bool) -> Self { - self.config = self.config.autopublish(value); - self - } - - /// Set whether the account is in testmode or not. - /// In testmode, the account skips publishing to the tangle. - #[cfg(test)] - #[must_use] - pub(crate) fn testmode(mut self, value: bool) -> Self { - self.config = self.config.testmode(value); - self - } - - /// Sets the account storage adapter. - #[must_use] - pub fn storage(mut self, value: S) -> Self { - self.storage = Some(Arc::new(value)); - self - } - - /// Sets the account storage adapter from a shared pointer. - #[must_use] - pub fn storage_shared(mut self, value: Arc) -> Self { - self.storage = Some(value); - self - } - - fn get_storage(&mut self) -> Arc { - if let Some(storage) = self.storage.as_ref() { - Arc::clone(storage) - } else { - // TODO: throw an error or log a warning that MemStore is not persistent and should not - // be used in production? - let storage: Arc = Arc::new(MemStore::new()); - self.storage = Some(Arc::clone(&storage)); - storage - } - } - - /// Sets the IOTA Tangle [`Client`], this determines the [`Network`][identity_iota_core_legacy::tangle::Network] - /// used by the identity. - /// [`Accounts`](Account) created by the same [`AccountBuilder`] will share the same [`Client`]. - /// - /// NOTE: this overwrites any [`ClientBuilder`] previously set by - /// [`AccountBuilder::client_builder`]. - #[must_use] - pub fn client(mut self, client: C) -> Self { - self.client = Some(client); - self.client_builder = None; - self - } - - /// Sets the IOTA Tangle [`Client`], this determines the [`Network`][identity_iota_core_legacy::tangle::Network] - /// used by the identity. - /// [`Accounts`](Account) created by the same [`AccountBuilder`] will share the same [`Client`]. - /// - /// NOTE: this overwrites any [`Client`] previously set by [`AccountBuilder::client`]. - #[must_use] - pub fn client_builder(mut self, client_builder: ClientBuilder) -> Self { - self.client = None; - self.client_builder = Some(client_builder); - self - } - - /// Returns a previously set [`Client`] or builds a new one based on the configuration passed - /// to [`AccountBuilder::client_builder`]. - /// - /// If neither is set, instantiates and stores a default [`Client`]. - async fn get_or_build_client(&mut self) -> Result { - if let Some(client) = &self.client { - Ok(C::clone(client)) - } else if let Some(client_builder) = self.client_builder.take() { - let client: C = C::from(client_builder.build().await?); - self.client = Some(C::clone(&client)); - Ok(client) - } else { - let client: C = C::from(Client::new().await?); - self.client = Some(C::clone(&client)); - Ok(client) - } - } - - async fn build_setup(&mut self) -> Result> { - let client: C = self.get_or_build_client().await?; - - Ok(AccountSetup::::new(self.get_storage(), client, self.config.clone())) - } - - /// Creates a new identity based on the builder configuration and returns - /// an [`Account`] instance to manage it. - /// - /// The identity is stored locally in the [`Storage`]. The DID network is automatically determined - /// by the [`Client`] used to publish it. - /// - /// See [`IdentitySetup`] to customize the identity creation. - pub async fn create_identity(&mut self, input: IdentitySetup) -> Result> { - let setup: AccountSetup = self.build_setup().await?; - Account::create_identity(setup, input).await - } - - /// Loads an existing identity with the specified `did` using the current builder configuration. - /// The identity must exist in the configured [`Storage`]. - /// - /// # Warning - /// - /// Callers are expected **not** to load the same [`IotaDID`] into more than one account, - /// as that would cause race conditions when updating the identity. - pub async fn load_identity(&mut self, did: IotaDID) -> Result> { - let setup: AccountSetup = self.build_setup().await?; - Account::load_identity(setup, did).await - } -} - -impl Default for AccountBuilder -where - C: SharedPtr, -{ - fn default() -> Self { - Self::new() - } -} diff --git a/identity_account/src/account/config.rs b/identity_account/src/account/config.rs deleted file mode 100644 index 0ff3a291f2..0000000000 --- a/identity_account/src/account/config.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::sync::Arc; - -use identity_account_storage::storage::Storage; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::SharedPtr; -use serde::Deserialize; -use serde::Serialize; - -/// A wrapper that holds configuration for an [`Account`] instantiation. -/// -/// The setup implements `Clone` so multiple [`Account`]s can be created -/// from the same setup. [`Storage`] and [`Client`] are shared among -/// those accounts, while the [`Config`] is unique to each account. -/// -/// [`Account`]([crate::account::Account]) -#[derive(Clone, Debug)] -pub(crate) struct AccountSetup> -where - C: SharedPtr, -{ - pub(crate) storage: Arc, - pub(crate) client: C, - pub(crate) config: AccountConfig, -} - -impl AccountSetup -where - C: SharedPtr, -{ - /// Create a new setup from the given [`Storage`] implementation - /// and with defaults for [`Config`] and [`Client`]. - pub(crate) fn new(storage: Arc, client: C, config: AccountConfig) -> Self { - Self { - storage, - client, - config, - } - } -} - -/// Configuration for [`Account`][crate::account::Account]s. -#[derive(Clone, Debug)] -pub(crate) struct AccountConfig { - pub(crate) autosave: AutoSave, - pub(crate) autopublish: bool, - pub(crate) testmode: bool, -} - -impl AccountConfig { - /// Creates a new default [`Config`]. - pub(crate) fn new() -> Self { - Self { - autosave: AutoSave::Every, - autopublish: true, - testmode: false, - } - } - - /// Sets the account auto-save behaviour. - /// - [`Every`][AutoSave::Every] => Save to storage on every update - /// - [`Never`][AutoSave::Never] => Never save to storage when updating - /// - [`Batch(n)`][AutoSave::Batch] => Save to storage after every `n` updates. - /// - /// Default: [`Every`][AutoSave::Every] - pub(crate) fn autosave(mut self, value: AutoSave) -> Self { - self.autosave = value; - self - } - - /// Sets the account auto-publish behaviour. - /// - `true` => publish to the Tangle on every DID document change - /// - `false` => never publish automatically - /// - /// Default: `true` - pub(crate) fn autopublish(mut self, value: bool) -> Self { - self.autopublish = value; - self - } - - /// Set whether the account is in testmode or not. - /// In testmode, the account skips publishing to the tangle. - #[cfg(test)] - pub(crate) fn testmode(mut self, value: bool) -> Self { - self.testmode = value; - self - } -} - -impl Default for AccountConfig { - fn default() -> Self { - Self::new() - } -} - -/// Available auto-save behaviours. -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub enum AutoSave { - /// Never save - Never, - /// Save after every action - Every, - /// Save after every N actions - Batch(usize), -} diff --git a/identity_account/src/account/mod.rs b/identity_account/src/account/mod.rs deleted file mode 100644 index 35f5369c8e..0000000000 --- a/identity_account/src/account/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![allow(clippy::module_inception)] - -mod account; -mod builder; -mod config; -mod publish_options; - -pub use self::account::*; -pub use self::builder::*; -pub use self::config::*; -pub use self::publish_options::*; diff --git a/identity_account/src/account/publish_options.rs b/identity_account/src/account/publish_options.rs deleted file mode 100644 index bf83e6d522..0000000000 --- a/identity_account/src/account/publish_options.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -/// Options to customize how identities are published to the Tangle. -#[derive(Debug, Clone)] -pub struct PublishOptions { - pub(crate) force_integration_update: bool, - pub(crate) sign_with: Option, -} - -impl PublishOptions { - /// Creates a new set of default publish options. - pub fn new() -> Self { - Self { - force_integration_update: false, - sign_with: None, - } - } - - /// Whether to force the publication to be an integration update. - /// - /// If this option is not set, the account automatically determines whether - /// an update needs to be published as an integration or a diff update. - /// Publishing as an integration update is always valid, but not recommended - /// for identities with many updates. - /// - /// See the IOTA DID method specification for more details. - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] - #[must_use] - pub fn force_integration_update(mut self, force: bool) -> Self { - self.force_integration_update = force; - self - } - - /// Set the fragment of a verification method with which to sign the update. - /// This must point to an Ed25519 method with a capability invocation - /// verification relationship. - /// - /// If this is not set, the method used is determined by - /// [`IotaDocument`][identity_iota_core_legacy::document::IotaDocument::default_signing_method] - #[must_use] - pub fn sign_with(mut self, fragment: impl Into) -> Self { - self.sign_with = Some(fragment.into()); - self - } -} - -impl Default for PublishOptions { - fn default() -> Self { - Self::new() - } -} diff --git a/identity_account/src/error.rs b/identity_account/src/error.rs deleted file mode 100644 index 82ffaee642..0000000000 --- a/identity_account/src/error.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! Errors that may occur when working with Identity Accounts. - -/// Alias for a `Result` with the error type [`Error`]. -pub type Result = ::core::result::Result; - -/// This type represents all possible errors that can occur in the library. -#[derive(Debug, thiserror::Error, strum::IntoStaticStr)] -pub enum Error { - /// Caused by errors from the [identity_core] crate. - #[error(transparent)] - CoreError(#[from] identity_core::Error), - /// Caused by errors from the [identity_did] crate. - #[error(transparent)] - DIDError(#[from] identity_did::Error), - /// Caused by errors from the [identity_credential] crate. - #[error(transparent)] - CredentialError(#[from] identity_credential::Error), - /// Caused by errors from the [identity_account_storage] crate. - #[error(transparent)] - AccountCoreError(#[from] identity_account_storage::Error), - /// Caused by errors from the [identity_iota_client_legacy] crate. - #[error(transparent)] - IotaClientError(#[from] identity_iota_client_legacy::Error), - /// Caused by errors from the [identity_iota_core_legacy] crate. - #[error(transparent)] - IotaCoreError(#[from] identity_iota_core_legacy::Error), - /// Caused by attempting to find an identity that does not exist. - #[error("Identity not found")] - IdentityNotFound, - /// Caused by attempting to perform an upate in an invalid context. - #[error("Update Error: {0}")] - UpdateError(#[from] crate::updates::UpdateError), - /// Caused by verification methods without fragments. - #[error("method missing fragment")] - MethodMissingFragment, - /// Caused by reaching an invalid state for the identity. - #[error("invalid identity state: {0}")] - InvalidIdentityState(String), -} - -impl From for Error { - fn from(error: identity_did::did::DIDError) -> Self { - identity_did::Error::from(error).into() - } -} diff --git a/identity_account/src/lib.rs b/identity_account/src/lib.rs deleted file mode 100644 index a8d8187895..0000000000 --- a/identity_account/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] -#![allow(deprecated)] -#![allow(clippy::upper_case_acronyms)] -#![doc = include_str!("./../README.md")] -#![warn( - rust_2018_idioms, - unreachable_pub, - // missing_docs, - // rustdoc::missing_crate_level_docs, - rustdoc::broken_intra_doc_links, - rustdoc::private_intra_doc_links, - rustdoc::private_doc_tests, - clippy::missing_safety_doc, - // clippy::missing_errors_doc -)] - -pub use self::error::Error; -pub use self::error::Result; - -#[cfg(test)] -mod tests; - -pub mod account; -pub mod error; -pub mod types; -pub mod updates; diff --git a/identity_account/src/tests/account.rs b/identity_account/src/tests/account.rs deleted file mode 100644 index 524fc6c342..0000000000 --- a/identity_account/src/tests/account.rs +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::pin::Pin; -use std::sync::Arc; - -use futures::Future; - -use identity_account_storage::identity::ChainState; -use identity_account_storage::storage::MemStore; -use identity_account_storage::storage::Stronghold; -use identity_core::common::Timestamp; -use identity_core::common::Url; -use identity_core::crypto::ProofOptions; -use identity_did::did::CoreDID; -use identity_did::utils::Queryable; -use identity_did::verification::MethodScope; -use identity_iota_client_legacy::chain::DocumentChain; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::ClientBuilder; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::diff::DiffMessage; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::tangle::MessageId; -use identity_iota_core_legacy::tangle::MessageIdExt; -use identity_iota_core_legacy::tangle::Network; - -use crate::account::Account; -use crate::account::AccountBuilder; -use crate::account::AccountConfig; -use crate::account::AccountSetup; -use crate::account::AutoSave; -use crate::account::PublishOptions; -use crate::types::IdentitySetup; -use crate::types::MethodContent; -use crate::Error; -use crate::Result; - -use super::util::*; - -#[tokio::test] -async fn test_account_builder() -> Result<()> { - let mut builder: AccountBuilder = AccountBuilder::default().testmode(true); - - let account1: Account = builder.create_identity(IdentitySetup::default()).await?; - - builder = builder.autopublish(false); - - let account2: Account = builder.create_identity(IdentitySetup::default()).await?; - - assert!(account1.autopublish()); - assert!(!account2.autopublish()); - - let did1 = account1.did().to_owned(); - let did2 = account2.did().to_owned(); - account2.delete_identity().await?; - - assert!(matches!( - builder.load_identity(did2).await.unwrap_err(), - crate::Error::IdentityNotFound - )); - - assert!(builder.load_identity(did1).await.is_ok()); - - Ok(()) -} - -#[tokio::test] -async fn test_account_chain_state() { - let mut builder: AccountBuilder = AccountBuilder::default().testmode(true); - - let mut account: Account = builder.create_identity(IdentitySetup::default()).await.unwrap(); - - let last_int_id = *account.chain_state().last_integration_message_id(); - - assert_ne!(last_int_id, MessageId::null()); - - // Assert that the last_diff_message_id is still null. - assert_eq!(account.chain_state().last_diff_message_id(), &MessageId::null()); - - // A diff update. - account - .update_identity() - .create_service() - .fragment("my-service-1") - .type_("MyCustomService") - .endpoint(Url::parse("https://example.com").unwrap()) - .apply() - .await - .unwrap(); - - // A diff update does not overwrite the int message id. - assert_eq!(&last_int_id, account.chain_state().last_integration_message_id()); - - // Assert that the last_diff_message_id was set. - assert_ne!(account.chain_state().last_diff_message_id(), &MessageId::null()); - - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("my-new-key") - .scope(MethodScope::capability_invocation()) - .apply() - .await - .unwrap(); - - // Int message id was overwritten. - assert_ne!(&last_int_id, account.chain_state().last_integration_message_id()); -} - -#[tokio::test] -async fn test_account_autopublish() { - // =========================================================================== - // Create, update and "publish" an identity - // =========================================================================== - - let config = AccountConfig::default().autopublish(false).testmode(true); - let client = ClientBuilder::new().node_sync_disabled().build().await.unwrap(); - let account_setup = AccountSetup::new(Arc::new(MemStore::new()), Arc::new(client), config); - - let mut account = Account::create_identity(account_setup, IdentitySetup::new()) - .await - .unwrap(); - - account - .update_identity() - .create_service() - .fragment("my-service") - .type_("LinkedDomains") - .endpoint(Url::parse("https://example.org").unwrap()) - .apply() - .await - .unwrap(); - - account - .update_identity() - .create_service() - .fragment("my-other-service") - .type_("LinkedDomains") - .endpoint(Url::parse("https://example.org").unwrap()) - .apply() - .await - .unwrap(); - - assert!(account.chain_state().last_integration_message_id().is_null()); - assert!(account.chain_state().last_diff_message_id().is_null()); - - account.publish().await.unwrap(); - - let last_int_message_id = *account.chain_state().last_integration_message_id(); - - assert!(!last_int_message_id.is_null()); - assert!(account.chain_state().last_diff_message_id().is_null()); - - // =========================================================================== - // Service assertions - // =========================================================================== - - let doc = account.document(); - - assert_eq!(doc.methods().len(), 1); - assert_eq!(doc.service().len(), 2); - - for service in ["my-service", "my-other-service"] { - assert!(doc.service().query(service).is_some()); - } - - // =========================================================================== - // More updates to the identity - // =========================================================================== - - account - .update_identity() - .delete_service() - .fragment("my-service") - .apply() - .await - .unwrap(); - - account - .update_identity() - .delete_service() - .fragment("my-other-service") - .apply() - .await - .unwrap(); - - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("new-method") - .apply() - .await - .unwrap(); - - account.publish().await.unwrap(); - - // No integration message was published - assert_eq!( - &last_int_message_id, - account.chain_state().last_integration_message_id() - ); - - let last_diff_message_id = *account.chain_state().last_diff_message_id(); - assert!(!last_diff_message_id.is_null()); - - // =========================================================================== - // Second round of assertions - // =========================================================================== - - let doc = account.document(); - - assert_eq!(doc.service().len(), 0); - assert_eq!(doc.methods().len(), 2); - - for method in ["sign-0", "new-method"] { - assert!(doc.resolve_method(method, None).is_some()); - } - - // =========================================================================== - // More updates to the identity - // =========================================================================== - - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("signing-key") - // Forces an integration update by adding a method able to update the document. - .scope(MethodScope::capability_invocation()) - .apply() - .await - .unwrap(); - - account.publish().await.unwrap(); - - // Another int update was published. - assert_ne!( - &last_int_message_id, - account.chain_state().last_integration_message_id() - ); - - // Diff message id was reset. - assert!(account.chain_state().last_diff_message_id().is_null()); -} - -#[tokio::test] -async fn test_account_publish_options_sign_with() { - let config = AccountConfig::default().autopublish(false).testmode(true); - let client = ClientBuilder::new().node_sync_disabled().build().await.unwrap(); - let account_config = AccountSetup::new(Arc::new(MemStore::new()), Arc::new(client), config); - - let auth_method = "auth-method"; - let signing_method = "singing-method-2"; - let invalid_signing_method = "invalid-signing-method"; - - let mut account = Account::create_identity(account_config, IdentitySetup::new()) - .await - .unwrap(); - - // Add an Authentication method unable to sign DID Document updates. - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment(auth_method) - .scope(MethodScope::authentication()) - .apply() - .await - .unwrap(); - - // Add a valid CapabilityInvocation method able to sign DID Document updates. - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment(signing_method) - .scope(MethodScope::capability_invocation()) - .apply() - .await - .unwrap(); - - // Add a CapabilityInvocation method unable to sign DID Document updates. - account - .update_identity() - .create_method() - .content(MethodContent::GenerateX25519) - .fragment(invalid_signing_method) - .scope(MethodScope::capability_invocation()) - .apply() - .await - .unwrap(); - - // INVALID: try sign with a non-existent method. - assert!(matches!( - account - .publish_with_options(PublishOptions::default().sign_with("non-existent-method")) - .await - .unwrap_err(), - Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( - identity_did::Error::MethodNotFound - )) - )); - - // INVALID: try sign with with an Authentication method. - assert!(matches!( - account - .publish_with_options(PublishOptions::default().sign_with(auth_method)) - .await - .unwrap_err(), - Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( - identity_did::Error::MethodNotFound - )) - )); - - // INVALID: try sign with a CapabilityInvocation method with an invalid MethodType. - assert!(matches!( - account - .publish_with_options(PublishOptions::default().sign_with(invalid_signing_method)) - .await - .unwrap_err(), - Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDocumentSigningMethodType), - )); - - assert!(account - .publish_with_options(PublishOptions::default().sign_with(signing_method)) - .await - .is_ok()); -} - -#[tokio::test] -async fn test_account_publish_options_force_integration() { - let config = AccountConfig::default().autopublish(false).testmode(true); - let client = ClientBuilder::new().node_sync_disabled().build().await.unwrap(); - let account_setup = AccountSetup::new(Arc::new(MemStore::new()), Arc::new(client), config); - let mut account = Account::create_identity(account_setup, IdentitySetup::new()) - .await - .unwrap(); - - account.publish().await.unwrap(); - - let last_int_id = *account.chain_state().last_integration_message_id(); - - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("test-auth") - .scope(MethodScope::authentication()) - .apply() - .await - .unwrap(); - - account - .publish_with_options(PublishOptions::default().force_integration_update(true)) - .await - .unwrap(); - - // Ensure update was published on integration chain. - assert_ne!(account.chain_state().last_integration_message_id(), &last_int_id); - assert_eq!(account.chain_state().last_diff_message_id(), &MessageId::null()); -} - -#[tokio::test] -async fn test_account_has_document_with_valid_signature_after_publication() { - let config = AccountConfig::default().autopublish(false).testmode(true); - let client = ClientBuilder::new().node_sync_disabled().build().await.unwrap(); - let account_setup = AccountSetup::new(Arc::new(MemStore::new()), Arc::new(client), config); - let mut account = Account::create_identity(account_setup, IdentitySetup::new()) - .await - .unwrap(); - - account.publish().await.unwrap(); - - let initial_doc: IotaDocument = account.document().to_owned(); - initial_doc.verify_document(account.document()).unwrap(); - - // Rotate signing methods. - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("another-sign") - .scope(MethodScope::capability_invocation()) - .apply() - .await - .unwrap(); - - account - .update_identity() - .delete_method() - .fragment(IotaDocument::DEFAULT_METHOD_FRAGMENT) - .apply() - .await - .unwrap(); - - // We have to force an integration update here, because in test mode, - // the account still publishes a diff update otherwise. - account - .publish_with_options(PublishOptions::default().force_integration_update(true)) - .await - .unwrap(); - - // Account document has a valid signature with respect to the initial doc. - initial_doc.verify_document(account.document()).unwrap(); - - account - .update_identity() - .create_method() - .content(MethodContent::GenerateEd25519) - .fragment("test-key-2") - .scope(MethodScope::authentication()) - .apply() - .await - .unwrap(); - - // If we don't publish, the document will not be signed. - initial_doc.verify_document(account.document()).unwrap_err(); -} - -#[tokio::test] -async fn test_account_sync_no_changes() -> Result<()> { - network_resilient_test(2, |n| { - Box::pin(async move { - let network = if n % 2 == 0 { Network::Devnet } else { Network::Mainnet }; - let mut account = create_account(network).await; - - // Case 0: Since nothing has been published to the tangle, read_document must return DID not found - assert!(account.fetch_document().await.is_err()); - - // Case 1: Tangle and account are synched - account.publish().await.unwrap(); - - let old_document: IotaDocument = account.document().clone(); - let old_chain_state: ChainState = account.chain_state().clone(); - - account.fetch_document().await.unwrap(); - - assert_eq!(&old_document, account.document()); - assert_eq!(&old_chain_state, account.chain_state()); - - // Case 2: Local document is ahead of the tangle - account - .update_identity() - .create_service() - .fragment("my-other-service") - .type_("LinkedDomains") - .endpoint(Url::parse("https://example.org").unwrap()) - .apply() - .await - .unwrap(); - - let old_document: IotaDocument = account.document().clone(); - let old_chain_state: ChainState = account.chain_state().clone(); - - account.fetch_document().await.unwrap(); - - assert_eq!(&old_document, account.document()); - assert_eq!(&old_chain_state, account.chain_state()); - - Ok(()) - }) - }) - .await - .unwrap(); - Ok(()) -} - -#[tokio::test] -async fn test_account_sync_integration_msg_update() { - network_resilient_test(2, |n| { - Box::pin(async move { - let network = if n % 2 == 0 { Network::Devnet } else { Network::Mainnet }; - let mut account = create_account(network.clone()).await; - account.publish().await.unwrap(); - - let client: Client = Client::builder().network(network).build().await.unwrap(); - let mut new_doc: IotaDocument = account.document().clone(); - new_doc.properties_mut_unchecked().insert("foo".into(), 123u32.into()); - new_doc.properties_mut_unchecked().insert("bar".into(), 456u32.into()); - new_doc.metadata.previous_message_id = *account.chain_state().last_integration_message_id(); - new_doc.metadata.updated = Some(Timestamp::now_utc()); - account - .sign( - IotaDocument::DEFAULT_METHOD_FRAGMENT, - &mut new_doc, - ProofOptions::default(), - ) - .await - .unwrap(); - client.publish_document(&new_doc).await.unwrap(); - let chain: DocumentChain = client.read_document_chain(account.did()).await.unwrap(); - - account.fetch_document().await.unwrap(); - assert!(account.document().properties().contains_key("foo")); - assert!(account.document().properties().contains_key("bar")); - assert_eq!( - account.chain_state().last_integration_message_id(), - chain.integration_message_id() - ); - assert_eq!(account.chain_state().last_diff_message_id(), chain.diff_message_id()); - // Ensure state was written into storage. - let storage_document: IotaDocument = account.load_document().await.unwrap(); - assert_eq!(&storage_document, account.document()); - Ok(()) - }) - }) - .await - .unwrap(); -} - -#[tokio::test] -async fn test_account_sync_diff_msg_update() { - network_resilient_test(2, |n| { - Box::pin(async move { - let network = if n % 2 == 0 { Network::Devnet } else { Network::Mainnet }; - let mut account = create_account(network.clone()).await; - account.publish().await.unwrap(); - - let client: Client = Client::builder().network(network).build().await.unwrap(); - let mut new_doc: IotaDocument = account.document().clone(); - new_doc.properties_mut_unchecked().insert("foo".into(), 123u32.into()); - new_doc.properties_mut_unchecked().insert("bar".into(), 456u32.into()); - new_doc.metadata.updated = Some(Timestamp::now_utc()); - let mut diff_msg: DiffMessage = DiffMessage::new( - account.document(), - &new_doc, - *account.chain_state().last_integration_message_id(), - ) - .unwrap(); - account - .sign( - IotaDocument::DEFAULT_METHOD_FRAGMENT, - &mut diff_msg, - ProofOptions::default(), - ) - .await - .unwrap(); - client - .publish_diff(account.chain_state().last_integration_message_id(), &diff_msg) - .await - .unwrap(); - let chain: DocumentChain = client.read_document_chain(account.did()).await.unwrap(); - - let old_chain_state: ChainState = account.chain_state().clone(); - account.fetch_document().await.unwrap(); - assert!(account.document().properties().contains_key("foo")); - assert!(account.document().properties().contains_key("bar")); - assert_eq!( - old_chain_state.last_integration_message_id(), - account.chain_state().last_integration_message_id() - ); - assert_eq!(account.chain_state().last_diff_message_id(), chain.diff_message_id()); - // Ensure state was written into storage. - let storage_document: IotaDocument = account.load_document().await.unwrap(); - assert_eq!(&storage_document, account.document()); - Ok(()) - }) - }) - .await - .unwrap(); -} - -async fn create_account(network: Network) -> Account { - Account::builder() - .storage( - Stronghold::new(&temporary_random_path(), "my-password".to_owned(), None) - .await - .unwrap(), - ) - .autopublish(false) - .autosave(AutoSave::Every) - .client_builder(ClientBuilder::new().network(network.clone())) - .create_identity(IdentitySetup::default()) - .await - .unwrap() -} - -// Repeats the test in the closure `test_runs` number of times. -// Network problems, i.e. a ClientError trigger a re-run. -// Other errors end the test immediately. -async fn network_resilient_test( - test_runs: u32, - f: impl Fn(u32) -> Pin>>>, -) -> Result<()> { - for test_run in 0..test_runs { - let test_attempt = f(test_run).await; - - match test_attempt { - error @ Err(Error::IotaClientError(identity_iota_client_legacy::Error::ClientError(_))) => { - eprintln!("test run {} errored with {:?}", test_run, error); - - if test_run == test_runs - 1 { - return error; - } - } - other => return other, - } - } - Ok(()) -} - -// Ensure that a future that contains an account is `Send` at compile-time. -#[tokio::test] -async fn test_assert_account_futures_are_send() -> Result<()> { - fn assert_future_send(_: T) {} - - let mut account: Account = AccountBuilder::default() - .testmode(true) - .create_identity(IdentitySetup::default()) - .await?; - - assert_future_send(account.update_identity().create_method().apply()); - - Ok(()) -} - -#[tokio::test] -async fn test_storage_index() { - for storage in storages().await { - let setup: AccountSetup = account_setup_storage(storage, Network::Mainnet).await; - - let account1 = Account::create_identity(setup.clone(), IdentitySetup::default()) - .await - .unwrap(); - - let index: Vec = account1.storage().did_list().await.unwrap(); - - assert_eq!(index.len(), 1); - assert!(index.contains(account1.did().as_ref())); - - let account2: Account = Account::create_identity(setup, IdentitySetup::default()).await.unwrap(); - - let index: Vec = account2.storage().did_list().await.unwrap(); - - assert_eq!(index.len(), 2); - assert!(index.contains(account1.did().as_ref())); - assert!(index.contains(account2.did().as_ref())); - - assert!(account2.storage().did_exists(account1.did().as_ref()).await.unwrap()); - assert!(account2.storage().did_exists(account2.did().as_ref()).await.unwrap()); - - let account1_did: IotaDID = account1.did().to_owned(); - account1.delete_identity().await.unwrap(); - - assert!(!account2.storage().did_exists(account1_did.as_ref()).await.unwrap()); - assert!(account2.storage().did_exists(account2.did().as_ref()).await.unwrap()); - assert_eq!(account2.storage().did_list().await.unwrap().len(), 1); - } -} diff --git a/identity_account/src/tests/mod.rs b/identity_account/src/tests/mod.rs deleted file mode 100644 index dd5c95a6e8..0000000000 --- a/identity_account/src/tests/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod account; -mod updates; -mod util; diff --git a/identity_account/src/tests/updates.rs b/identity_account/src/tests/updates.rs deleted file mode 100644 index cedadecd62..0000000000 --- a/identity_account/src/tests/updates.rs +++ /dev/null @@ -1,754 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::sync::Arc; - -use identity_account_storage::storage::MemStore; -use identity_account_storage::types::KeyLocation; -use identity_core::common::OneOrSet; -use identity_core::common::OrderedSet; -use identity_core::common::Timestamp; -use identity_core::common::Url; -use identity_core::convert::FromJson; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_core::crypto::PrivateKey; -use identity_core::crypto::PublicKey; -use identity_did::did::DID; -use identity_did::service::ServiceEndpoint; -use identity_did::utils::Queryable; -use identity_did::verification::MethodRelationship; -use identity_did::verification::MethodScope; -use identity_did::verification::MethodType; -use identity_iota_client_legacy::tangle::ClientBuilder; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::document::IotaVerificationMethod; -use identity_iota_core_legacy::tangle::Network; - -use crate::account::Account; -use crate::account::AccountConfig; -use crate::account::AccountSetup; -use crate::error::Error; -use crate::error::Result; -use crate::types::IdentitySetup; -use crate::types::IdentityState; -use crate::types::MethodContent; -use crate::updates::Update; -use crate::updates::UpdateError; - -use super::util::*; - -#[tokio::test] -async fn test_create_identity() -> Result<()> { - for storage in storages().await { - let account = Account::create_identity( - account_setup_storage(storage, Network::Mainnet).await, - IdentitySetup::default(), - ) - .await - .unwrap(); - - let document: &IotaDocument = account.document(); - - let expected_fragment = IotaDocument::DEFAULT_METHOD_FRAGMENT; - let method: &IotaVerificationMethod = document.resolve_method(expected_fragment, None).unwrap(); - - assert_eq!(document.core_document().verification_relationships().count(), 1); - assert_eq!(document.core_document().methods(None).len(), 1); - - let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); - - // Ensure we can retrieve the correct location for the key. - assert_eq!( - location, - KeyLocation::new( - KeyType::Ed25519, - expected_fragment.to_owned(), - method.data().try_decode().unwrap().as_ref() - ) - ); - - // Ensure the key exists in storage. - assert!(account - .storage() - .key_exists(account.did().as_ref(), &location) - .await - .unwrap()); - - // Ensure the state was written to storage. - assert!(account.load_document().await.is_ok()); - - // Ensure timestamps were recently set. - assert!(document.metadata.created.unwrap() > Timestamp::from_unix(Timestamp::now_utc().to_unix() - 15).unwrap()); - assert!(document.metadata.updated.unwrap() > Timestamp::from_unix(Timestamp::now_utc().to_unix() - 15).unwrap()); - - // Ensure the DID was added to the index. - assert!(account.storage().did_exists(account.did().as_ref()).await.unwrap()); - } - - Ok(()) -} - -#[tokio::test] -async fn test_create_identity_network() -> Result<()> { - // Ensure created identities match the client network. - - // Mainnet - let account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - assert_eq!(account.did().network_str(), Network::Mainnet.name_str()); - - // Devnet - let account = Account::create_identity(account_setup(Network::Devnet).await, IdentitySetup::default()).await?; - assert_eq!(account.did().network_str(), Network::Devnet.name_str()); - - // Private Tangle - let account = Account::create_identity( - AccountSetup::new( - Arc::new(MemStore::new()), - Arc::new( - ClientBuilder::new() - .network(Network::try_from_name("custom")?) - .node("http://127.0.0.1:8082")? - .node_sync_disabled() - .build() - .await - .unwrap(), - ), - AccountConfig::new().testmode(true), - ), - IdentitySetup::default(), - ) - .await?; - assert_eq!(account.did().network_str(), "custom"); - - Ok(()) -} - -#[tokio::test] -async fn test_create_identity_already_exists() -> Result<()> { - for storage in storages().await { - let keypair = KeyPair::new(KeyType::Ed25519)?; - let identity_create = IdentitySetup::default().private_key(keypair.private().clone()); - - let account_setup = account_setup_storage(storage, Network::Mainnet).await; - - let account = Account::create_identity(account_setup.clone(), identity_create.clone()) - .await - .unwrap(); - - let initial_state: Vec = account_setup.storage.blob_get(account.did().as_ref()).await?.unwrap(); - let initial_state: IdentityState = IdentityState::from_json_slice(&initial_state).unwrap(); - - let output = Account::create_identity(account_setup.clone(), identity_create).await; - - assert!(matches!( - output.unwrap_err(), - Error::AccountCoreError(identity_account_storage::Error::IdentityAlreadyExists) - )); - - // Ensure nothing was overwritten in storage - let account_state: Vec = account_setup.storage.blob_get(account.did().as_ref()).await?.unwrap(); - let account_state: IdentityState = IdentityState::from_json_slice(&account_state).unwrap(); - assert_eq!(initial_state.document()?, account_state.document()?); - } - Ok(()) -} - -#[tokio::test] -async fn test_create_identity_from_invalid_private_key() -> Result<()> { - let private_bytes: Box<[u8]> = Box::new([0; 33]); - let private_key: PrivateKey = PrivateKey::from(private_bytes); - - let id_create = IdentitySetup::new().private_key(private_key); - - let err = Account::create_identity(account_setup(Network::Mainnet).await, id_create) - .await - .unwrap_err(); - - assert!(matches!(err, Error::UpdateError(UpdateError::InvalidMethodContent(_)))); - - Ok(()) -} - -#[tokio::test] -async fn test_create_method_content_generate() -> Result<()> { - for storage in storages().await { - for method_content in [MethodContent::GenerateEd25519, MethodContent::GenerateX25519] { - let mut account: Account = Account::create_identity( - account_setup_storage(Arc::clone(&storage), Network::Mainnet).await, - IdentitySetup::default(), - ) - .await?; - - let initial_document: IotaDocument = account.document().to_owned(); - - let method_type: MethodType = method_content.method_type(); - let key_type: KeyType = method_content.key_type(); - let fragment = "key-1".to_owned(); - let update: Update = Update::CreateMethod { - scope: MethodScope::default(), - content: method_content, - fragment: fragment.clone(), - }; - - account.process_update(update).await.unwrap(); - - let document: &IotaDocument = account.document(); - - // Ensure existence. - let method: &IotaVerificationMethod = document.resolve_method(&fragment, None).unwrap(); - - // Ensure key type. - assert_eq!(method.type_(), method_type); - - // Still only the default relationship. - assert_eq!(document.core_document().verification_relationships().count(), 1); - assert_eq!(document.core_document().methods(None).len(), 2); - - let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); - - // Ensure we can retrieve the correct location for the key. - assert_eq!( - location, - KeyLocation::new(key_type, fragment, method.data().try_decode().unwrap().as_ref()) - ); - - // Ensure the key exists in storage. - assert!(account - .storage() - .key_exists(account.did().as_ref(), &location) - .await - .unwrap()); - - // Ensure `created` wasn't updated. - assert_eq!(initial_document.metadata.created, document.metadata.created); - - // Ensure `updated` was recently set. - assert!(document.metadata.updated.unwrap() > Timestamp::from_unix(Timestamp::now_utc().to_unix() - 15).unwrap()); - } - } - Ok(()) -} - -#[tokio::test] -async fn test_create_method_content_public() -> Result<()> { - let bytes: [u8; 32] = [1_u8; 32]; - for method_content in [ - MethodContent::PublicX25519(PublicKey::from(bytes.to_vec())), - MethodContent::PublicEd25519(PublicKey::from(bytes.to_vec())), - ] { - let mut account: Account = - Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - let method_type: MethodType = method_content.method_type(); - let fragment = "key-1".to_owned(); - let update: Update = Update::CreateMethod { - scope: MethodScope::default(), - content: method_content, - fragment: fragment.clone(), - }; - account.process_update(update).await?; - - // Ensure existence and type. - let method: &IotaVerificationMethod = account.document().resolve_method(&fragment, None).unwrap(); - assert_eq!(method.type_(), method_type); - assert_eq!(method.data().try_decode().unwrap().as_slice(), bytes); - - // Ensure no key exists in storage. - let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); - assert!(!account - .storage() - .key_exists(account.did().as_ref(), &location) - .await - .unwrap()); - } - Ok(()) -} - -#[tokio::test] -async fn test_create_scoped_method() -> Result<()> { - for scope in &[ - MethodScope::assertion_method(), - MethodScope::authentication(), - MethodScope::capability_delegation(), - MethodScope::capability_invocation(), - MethodScope::key_agreement(), - ] { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - let fragment = "#key-1".to_owned(); - - let update: Update = Update::CreateMethod { - scope: *scope, - content: MethodContent::GenerateEd25519, - fragment: fragment.clone(), - }; - - account.process_update(update).await?; - - let document: &IotaDocument = account.document(); - - assert_eq!(document.core_document().verification_relationships().count(), 2); - - assert_eq!(document.core_document().methods(None).len(), 2); - - let core_doc = document.core_document(); - - let contains = match scope { - MethodScope::VerificationRelationship(MethodRelationship::Authentication) => core_doc - .resolve_method(&fragment, Some(MethodScope::authentication())) - .is_some(), - MethodScope::VerificationRelationship(MethodRelationship::AssertionMethod) => core_doc - .resolve_method(&fragment, Some(MethodScope::assertion_method())) - .is_some(), - MethodScope::VerificationRelationship(MethodRelationship::KeyAgreement) => core_doc - .resolve_method(&fragment, Some(MethodScope::key_agreement())) - .is_some(), - MethodScope::VerificationRelationship(MethodRelationship::CapabilityDelegation) => core_doc - .resolve_method(&fragment, Some(MethodScope::capability_delegation())) - .is_some(), - MethodScope::VerificationRelationship(MethodRelationship::CapabilityInvocation) => core_doc - .resolve_method(&fragment, Some(MethodScope::capability_invocation())) - .is_some(), - _ => unreachable!(), - }; - - assert!(contains); - } - - Ok(()) -} - -#[tokio::test] -async fn test_create_method_duplicate_fragment() { - let mut account_setup = account_setup(Network::Mainnet).await; - account_setup.config = account_setup.config.testmode(true).autopublish(false); - - let mut account = Account::create_identity(account_setup, IdentitySetup::default()) - .await - .unwrap(); - - let update: Update = Update::CreateMethod { - scope: MethodScope::default(), - content: MethodContent::GenerateEd25519, - fragment: "key-1".to_owned(), - }; - - account.process_update(update.clone()).await.unwrap(); - - let output = account.process_update(update.clone()).await; - - // Attempting to add a method with the same fragment. - assert!(matches!( - output.unwrap_err(), - Error::DIDError(identity_did::Error::MethodAlreadyExists) - )); -} - -#[tokio::test] -async fn test_create_method_from_private_key() { - for storage in storages().await { - let mut account = Account::create_identity( - account_setup_storage(storage, Network::Mainnet).await, - IdentitySetup::default(), - ) - .await - .unwrap(); - - let keypair = KeyPair::new(KeyType::Ed25519).unwrap(); - let fragment = "key-1".to_owned(); - - let update: Update = Update::CreateMethod { - scope: MethodScope::default(), - content: MethodContent::PrivateEd25519(keypair.private().clone()), - fragment: fragment.clone(), - }; - - account.process_update(update).await.unwrap(); - - let document: &IotaDocument = account.document(); - - let method: &IotaVerificationMethod = document.resolve_method(&fragment, None).unwrap(); - - let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); - - let public_key = account - .storage() - .key_public(account.did().as_ref(), &location) - .await - .unwrap(); - - assert_eq!(public_key.as_ref(), keypair.public().as_ref()); - } -} - -#[tokio::test] -async fn test_create_method_from_invalid_private_key() -> Result<()> { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - let private_bytes: Box<[u8]> = Box::new([0; 33]); - let private_key = PrivateKey::from(private_bytes); - - let update: Update = Update::CreateMethod { - scope: MethodScope::default(), - content: MethodContent::PrivateEd25519(private_key), - fragment: "key-1".to_owned(), - }; - - let err = account.process_update(update).await.unwrap_err(); - - assert!(matches!(err, Error::UpdateError(UpdateError::InvalidMethodContent(_)))); - - Ok(()) -} - -#[tokio::test] -async fn test_attach_method_relationship() -> Result<()> { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - let fragment = "key-1".to_owned(); - - let update: Update = Update::CreateMethod { - scope: MethodScope::default(), - content: MethodContent::GenerateEd25519, - fragment: fragment.clone(), - }; - - account.process_update(update).await?; - - // One relationship by default. - assert_eq!( - account.document().core_document().verification_relationships().count(), - 1 - ); - - let default_method_fragment = account - .document() - .default_signing_method() - .unwrap() - .id() - .fragment() - .unwrap() - .to_owned(); - - // Attempt attaching a relationship to an embedded method. - let update: Update = Update::AttachMethodRelationship { - relationships: vec![MethodRelationship::AssertionMethod, MethodRelationship::KeyAgreement], - fragment: default_method_fragment, - }; - - let err = account.process_update(update).await.unwrap_err(); - - assert!(matches!( - err, - Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( - identity_did::Error::InvalidMethodEmbedded - )) - )); - - // No relationships were created. - assert_eq!( - account.document().core_document().verification_relationships().count(), - 1 - ); - - assert_eq!(account.document().core_document().assertion_method().iter().count(), 0); - assert_eq!(account.document().core_document().key_agreement().iter().count(), 0); - - let update: Update = Update::AttachMethodRelationship { - relationships: vec![MethodRelationship::AssertionMethod, MethodRelationship::KeyAgreement], - fragment: fragment.clone(), - }; - - account.process_update(update).await?; - - // Relationships were created. - assert_eq!( - account.document().core_document().verification_relationships().count(), - 3 - ); - - assert_eq!(account.document().core_document().assertion_method().len(), 1); - assert_eq!( - account - .document() - .core_document() - .assertion_method() - .first() - .unwrap() - .id() - .fragment() - .unwrap(), - fragment - ); - assert_eq!(account.document().core_document().key_agreement().len(), 1); - assert_eq!( - account - .document() - .core_document() - .key_agreement() - .first() - .unwrap() - .id() - .fragment() - .unwrap(), - fragment - ); - - Ok(()) -} - -#[tokio::test] -async fn test_detach_method_relationship() -> Result<()> { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - let generic_fragment = "key-1".to_owned(); - let embedded_fragment = "embedded-1".to_owned(); - - // Add an embedded method. - let update: Update = Update::CreateMethod { - scope: MethodScope::authentication(), - content: MethodContent::GenerateEd25519, - fragment: embedded_fragment.clone(), - }; - - account.process_update(update).await?; - - // Attempt detaching a relationship from an embedded method. - let update: Update = Update::DetachMethodRelationship { - relationships: vec![MethodRelationship::Authentication], - fragment: embedded_fragment, - }; - - let err = account.process_update(update).await.unwrap_err(); - - assert!(matches!( - err, - Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( - identity_did::Error::InvalidMethodEmbedded - )) - )); - - // No relationships were removed. - assert_eq!( - account.document().core_document().verification_relationships().count(), - 2 - ); - - let update: Update = Update::CreateMethod { - scope: MethodScope::default(), - content: MethodContent::GenerateEd25519, - fragment: generic_fragment.clone(), - }; - - account.process_update(update).await?; - - let update: Update = Update::AttachMethodRelationship { - relationships: vec![MethodRelationship::AssertionMethod, MethodRelationship::KeyAgreement], - fragment: generic_fragment.clone(), - }; - - account.process_update(update).await?; - - assert_eq!(account.document().core_document().assertion_method().len(), 1); - assert_eq!(account.document().core_document().key_agreement().len(), 1); - - let update: Update = Update::DetachMethodRelationship { - relationships: vec![MethodRelationship::AssertionMethod, MethodRelationship::KeyAgreement], - fragment: generic_fragment.clone(), - }; - - account.process_update(update).await?; - - assert_eq!(account.document().core_document().assertion_method().len(), 0); - assert_eq!(account.document().core_document().key_agreement().len(), 0); - - Ok(()) -} - -#[tokio::test] -async fn test_delete_method() -> Result<()> { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - let fragment = "key-1".to_owned(); - let initial_document = account.document().to_owned(); - let content: MethodContent = MethodContent::GenerateEd25519; - - let update: Update = Update::CreateMethod { - scope: MethodScope::default(), - content: content.clone(), - fragment: fragment.clone(), - }; - - account.process_update(update).await?; - - // Ensure it was added. - let method: &IotaVerificationMethod = account.document().resolve_method(&fragment, None).unwrap(); - let location: KeyLocation = KeyLocation::from_verification_method(method).unwrap(); - - let update: Update = Update::DeleteMethod { - fragment: "key-1".to_owned(), - }; - - account.process_update(update.clone()).await?; - - let document: &IotaDocument = account.document(); - - // Ensure it no longer exists. - assert!(document.resolve_method(&fragment, None).is_none()); - - // Still only the default relationship. - assert_eq!(document.core_document().verification_relationships().count(), 1); - - assert_eq!(document.core_document().methods(None).len(), 1); - - // Ensure the key still exists in storage. - assert!(account - .storage() - .key_exists(account.did().as_ref(), &location) - .await - .unwrap()); - - // Ensure `created` wasn't updated. - assert_eq!(initial_document.metadata.created, document.metadata.created); - // Ensure `updated` was recently set. - assert!(document.metadata.updated.unwrap() > Timestamp::from_unix(Timestamp::now_utc().to_unix() - 15).unwrap()); - - // Deleting a non-existing methods fails. - let output = account.process_update(update).await; - - assert!(matches!( - output.unwrap_err(), - Error::IotaCoreError(identity_iota_core_legacy::Error::InvalidDoc( - identity_did::Error::MethodNotFound - )) - )); - - Ok(()) -} - -#[tokio::test] -async fn test_insert_service() -> Result<()> { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - assert_eq!(account.document().service().len(), 0); - - let fragment = "#service-42".to_owned(); - - let update: Update = Update::CreateService { - fragment: fragment.clone(), - types: vec!["LinkedDomains".to_owned()], - endpoint: ServiceEndpoint::One(Url::parse("https://iota.org").unwrap()), - properties: None, - }; - - account.process_update(update.clone()).await?; - - assert_eq!(account.document().service().len(), 1); - - // Ensure the service can be queried. - let service_url = account.did().to_url().join(fragment).unwrap(); - assert!(account.document().service().query(service_url).is_some()); - - let err = account.process_update(update.clone()).await.unwrap_err(); - - assert!(matches!( - err, - Error::UpdateError(UpdateError::DuplicateServiceFragment(_)) - )); - - Ok(()) -} - -#[tokio::test] -async fn test_remove_service() -> Result<()> { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - let fragment = "#service-42".to_owned(); - - let update: Update = Update::CreateService { - fragment: fragment.clone(), - types: vec!["LinkedDomains".to_owned()], - endpoint: ServiceEndpoint::One(Url::parse("https://iota.org").unwrap()), - properties: None, - }; - - account.process_update(update).await.unwrap(); - - assert_eq!(account.document().service().len(), 1); - - let update: Update = Update::DeleteService { - fragment: fragment.clone(), - }; - - account.process_update(update.clone()).await.unwrap(); - - assert_eq!(account.document().service().len(), 0); - - // Attempting to remove a non-existing service returns an error. - let err = account.process_update(update).await.unwrap_err(); - - assert!(matches!(err, Error::UpdateError(UpdateError::ServiceNotFound))); - - Ok(()) -} - -#[tokio::test] -async fn test_set_controller() -> Result<()> { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - let keypair1: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let iota_did1: IotaDID = IotaDID::new(keypair1.public().as_ref()).unwrap(); - - let keypair2: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let iota_did2: IotaDID = IotaDID::new(keypair2.public().as_ref()).unwrap(); - - // Set one controller. - let update: Update = Update::SetController { - controllers: Some(OneOrSet::new_one(iota_did1.clone())), - }; - account.process_update(update).await.unwrap(); - assert_eq!(account.document().controller().unwrap().len(), 1); - - // Set two controllers. - let set: OrderedSet = OrderedSet::from_iter(vec![iota_did1, iota_did2]); - let update: Update = Update::SetController { - controllers: Some(OneOrSet::new_set(set).unwrap()), - }; - account.process_update(update).await.unwrap(); - assert_eq!(account.document().controller().unwrap().len(), 2); - - // Remove all controllers. - let update: Update = Update::SetController { controllers: None }; - account.process_update(update).await.unwrap(); - assert_eq!(account.document().controller(), None); - - Ok(()) -} - -#[tokio::test] -async fn test_set_also_known_as() -> Result<()> { - let mut account = Account::create_identity(account_setup(Network::Mainnet).await, IdentitySetup::default()).await?; - - // No elements by default. - assert_eq!(account.document().also_known_as().len(), 0); - - // Set two Urls. - let urls: OrderedSet = OrderedSet::from_iter(vec![ - Url::parse("did:iota:xyz").unwrap(), - Url::parse("did:iota:abc").unwrap(), - ]); - let update: Update = Update::SetAlsoKnownAs { urls }; - account.process_update(update).await.unwrap(); - assert_eq!(account.document().also_known_as().len(), 2); - - // Remove all Urls. - let update: Update = Update::SetAlsoKnownAs { - urls: OrderedSet::new(), - }; - account.process_update(update).await.unwrap(); - assert_eq!(account.document().also_known_as().len(), 0); - - Ok(()) -} diff --git a/identity_account/src/tests/util.rs b/identity_account/src/tests/util.rs deleted file mode 100644 index 3923b1a260..0000000000 --- a/identity_account/src/tests/util.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::sync::Arc; - -use identity_account_storage::storage::MemStore; -use identity_account_storage::storage::Storage; -use identity_account_storage::storage::Stronghold; -use identity_iota_client_legacy::tangle::ClientBuilder; -use identity_iota_core_legacy::tangle::Network; -use rand::distributions::DistString; -use rand::rngs::OsRng; - -use crate::account::AccountConfig; -use crate::account::AccountSetup; - -pub(super) async fn account_setup(network: Network) -> AccountSetup { - account_setup_storage(Arc::new(MemStore::new()), network).await -} - -pub(super) async fn account_setup_storage(storage: Arc, network: Network) -> AccountSetup { - AccountSetup::new( - storage, - Arc::new( - ClientBuilder::new() - .network(network) - .node_sync_disabled() - .build() - .await - .unwrap(), - ), - AccountConfig::new().testmode(true), - ) -} - -pub(super) fn temporary_random_path() -> String { - let mut file = std::env::temp_dir(); - file.push("test_strongholds"); - file.push(rand::distributions::Alphanumeric.sample_string(&mut OsRng, 32)); - file.set_extension("stronghold"); - file.to_str().unwrap().to_owned() -} - -pub(super) async fn storages() -> [Arc; 2] { - [ - Arc::new(MemStore::new()), - Arc::new( - Stronghold::new(&temporary_random_path(), "password".to_owned(), Some(false)) - .await - .unwrap(), - ), - ] -} diff --git a/identity_account/src/types/identity_setup.rs b/identity_account/src/types/identity_setup.rs deleted file mode 100644 index 45ae236d6e..0000000000 --- a/identity_account/src/types/identity_setup.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_core::crypto::PrivateKey; - -/// Configuration used to create a new Identity. -#[derive(Clone, Debug)] -pub struct IdentitySetup { - /// Use a pre-generated Ed25519 private key for the DID. - pub(crate) private_key: Option, -} - -impl IdentitySetup { - /// Creates a new `IdentitySetup` instance. - pub const fn new() -> Self { - Self { private_key: None } - } - - /// Sets the Ed25519 private key to use for Identity creation. - #[must_use] - pub fn private_key(mut self, value: PrivateKey) -> Self { - self.private_key = Some(value); - self - } -} - -impl Default for IdentitySetup { - fn default() -> Self { - Self::new() - } -} diff --git a/identity_account/src/types/identity_state.rs b/identity_account/src/types/identity_state.rs deleted file mode 100644 index b24e04103d..0000000000 --- a/identity_account/src/types/identity_state.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_account_storage::identity::ChainState; -use identity_core::convert::FromJson; -use identity_core::convert::ToJson; -use identity_iota_core_legacy::document::IotaDocument; -use serde::Deserialize; -use serde::Serialize; - -use crate::error::Result; - -/// Holds the internal state for the identity. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub(crate) struct IdentityState { - version: StateVersion, - document: Option>, - chain_state: Option>, -} - -impl IdentityState { - /// Creates a new [`IdentityState`]. - pub(crate) fn new(document: Option<&IotaDocument>, chain_state: Option<&ChainState>) -> Result { - let document: Option> = document.map(|iota_doc| iota_doc.to_json_vec()).transpose()?; - let chain_state: Option> = chain_state.map(|chain_state| chain_state.to_json_vec()).transpose()?; - Ok(IdentityState { - version: StateVersion::default(), - document, - chain_state, - }) - } - - /// Returns the deserialized [`IotaDocument`]. - pub(crate) fn document(&self) -> Result> { - match self.version { - StateVersion::V1 => Ok(self.document.as_ref().map(IotaDocument::from_json_slice).transpose()?), - } - } - - /// Returns the deserialized [`ChainState`]. - pub(crate) fn chain_state(&self) -> Result> { - match self.version { - StateVersion::V1 => Ok(self.chain_state.as_ref().map(ChainState::from_json_slice).transpose()?), - } - } - - #[allow(dead_code)] - /// Returns the [`StateVersion`] of the [`IdentityState`]. - pub(crate) fn version(&self) -> StateVersion { - self.version - } -} - -#[derive(Copy, Clone, Debug, Default, PartialEq, Deserialize, Serialize)] -pub(crate) enum StateVersion { - #[default] - V1 = 1, -} diff --git a/identity_account/src/types/identity_updater.rs b/identity_account/src/types/identity_updater.rs deleted file mode 100644 index d1baf690f5..0000000000 --- a/identity_account/src/types/identity_updater.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::SharedPtr; - -use crate::account::Account; - -/// A struct created by the [`Account::update_identity`] method, that -/// allows executing various updates on the identity it was created on. -#[derive(Debug)] -pub struct IdentityUpdater<'account, C> -where - C: SharedPtr, -{ - pub(crate) account: &'account mut Account, -} - -impl<'account, C> IdentityUpdater<'account, C> -where - C: SharedPtr, -{ - pub(crate) fn new(account: &'account mut Account) -> Self { - Self { account } - } -} diff --git a/identity_account/src/types/method_content.rs b/identity_account/src/types/method_content.rs deleted file mode 100644 index 92df48711d..0000000000 --- a/identity_account/src/types/method_content.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_core::crypto::KeyType; -use identity_core::crypto::PrivateKey; -use identity_core::crypto::PublicKey; - -/// Method content for creating new verification methods. -#[non_exhaustive] -#[derive(Clone, Debug)] -pub enum MethodContent { - /// Generate and store a new Ed25519 keypair for a new - /// [`Ed25519VerificationKey2018`](identity_did::verification::MethodType::Ed25519VerificationKey2018) - /// method. - GenerateEd25519, - /// Store an existing Ed25519 private key and derive a public key from it for a new - /// [`Ed25519VerificationKey2018`](identity_did::verification::MethodType::Ed25519VerificationKey2018) - /// method. - PrivateEd25519(PrivateKey), - /// Insert an existing Ed25519 public key into a new - /// [`Ed25519VerificationKey2018`](identity_did::verification::MethodType::Ed25519VerificationKey2018) - /// method, without generating or storing a private key. - /// - /// NOTE: the method will be unable to be used to sign anything without a private key. - PublicEd25519(PublicKey), - /// Generate and store a new X25519 keypair for a new - /// [`X25519KeyAgreementKey2019`](identity_did::verification::MethodType::X25519KeyAgreementKey2019) - /// method. - GenerateX25519, - /// Store an existing X25519 private key and derive a public key from it for a new - /// [`X25519KeyAgreementKey2019`](identity_did::verification::MethodType::X25519KeyAgreementKey2019) - /// method. - PrivateX25519(PrivateKey), - /// Insert an existing X25519 public key into a new - /// [`X25519KeyAgreementKey2019`](identity_did::verification::MethodType::X25519KeyAgreementKey2019) - /// method, without generating or storing a private key. - /// - /// NOTE: the method will be unable to be used for key exchange without a private key. - PublicX25519(PublicKey), -} - -impl MethodContent { - /// Returns the [`MethodType`](identity_did::verification::MethodType) associated with the `MethodContent` variant. - #[cfg(test)] - pub(crate) fn method_type(&self) -> identity_did::verification::MethodType { - match self { - MethodContent::GenerateEd25519 => identity_did::verification::MethodType::Ed25519VerificationKey2018, - MethodContent::PrivateEd25519(_) => identity_did::verification::MethodType::Ed25519VerificationKey2018, - MethodContent::PublicEd25519(_) => identity_did::verification::MethodType::Ed25519VerificationKey2018, - MethodContent::GenerateX25519 => identity_did::verification::MethodType::X25519KeyAgreementKey2019, - MethodContent::PrivateX25519(_) => identity_did::verification::MethodType::X25519KeyAgreementKey2019, - MethodContent::PublicX25519(_) => identity_did::verification::MethodType::X25519KeyAgreementKey2019, - } - } - - /// Returns the [`KeyType`] associated with the `MethodContent` variant. - pub(crate) fn key_type(&self) -> KeyType { - match self { - MethodContent::GenerateEd25519 => KeyType::Ed25519, - MethodContent::PrivateEd25519(_) => KeyType::Ed25519, - MethodContent::PublicEd25519(_) => KeyType::Ed25519, - MethodContent::GenerateX25519 => KeyType::X25519, - MethodContent::PrivateX25519(_) => KeyType::X25519, - MethodContent::PublicX25519(_) => KeyType::X25519, - } - } -} diff --git a/identity_account/src/types/mod.rs b/identity_account/src/types/mod.rs deleted file mode 100644 index b801500afa..0000000000 --- a/identity_account/src/types/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use self::identity_setup::*; -pub(crate) use self::identity_state::IdentityState; -pub use self::identity_updater::*; -pub use self::method_content::*; - -mod identity_setup; -mod identity_state; -mod identity_updater; -mod method_content; diff --git a/identity_account/src/updates/error.rs b/identity_account/src/updates/error.rs deleted file mode 100644 index ba05a40cc1..0000000000 --- a/identity_account/src/updates/error.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_account_storage::types::KeyLocation; -use identity_did::verification::MethodType; - -/// Errors than may occur while processing an update in the [`Account`][crate::account::Account]. -#[derive(Debug, thiserror::Error)] -pub enum UpdateError { - #[error("document already exists")] - DocumentAlreadyExists, - #[error("service not found")] - ServiceNotFound, - #[error("invalid method type - {}", .0.as_str())] - InvalidMethodType(MethodType), - #[error("invalid method fragment - {0}")] - InvalidMethodFragment(&'static str), - #[error("invalid method content: {0}")] - InvalidMethodContent(String), - #[error("missing required field - {0}")] - MissingRequiredField(&'static str), - #[error("duplicate key location - {0}")] - DuplicateKeyLocation(KeyLocation), - #[error("duplicate service fragment - {0}")] - DuplicateServiceFragment(String), -} diff --git a/identity_account/src/updates/macros.rs b/identity_account/src/updates/macros.rs deleted file mode 100644 index b56b595745..0000000000 --- a/identity_account/src/updates/macros.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -macro_rules! ensure { - ($cond:expr, $error:expr $(,)?) => { - if !$cond { - return Err($crate::Error::UpdateError($error)); - } - }; -} - -macro_rules! impl_update_builder { - (@finish $this:ident optional $field:ident $ty:ty) => { - $this.$field - }; - (@finish $this:ident default $field:ident $ty:ty) => { - $this.$field.unwrap_or_default() - }; - (@finish $this:ident defaulte $field:ident $ty:ty = $variant:ident) => { - $this.$field.unwrap_or(<$ty>::$variant) - }; - (@finish $this:ident defaultv $field:ident $ty:ty = $value:expr) => { - $this.$field.unwrap_or_else(|| $value) - }; - (@finish $this:ident required $field:ident $ty:ty) => { - match $this.$field { - Some(value) => value, - None => return Err($crate::Error::UpdateError( - $crate::updates::UpdateError::MissingRequiredField(stringify!($field)), - )), - } - }; - ($(#[$doc:meta])* $ident:ident { $(@ $requirement:ident $field:ident $ty:ty $(= $value:expr)?),* $(,)* }) => { - paste::paste! { - $(#[$doc])* - #[derive(Debug)] - pub struct [<$ident Builder>]<'account, C> - where - C: identity_iota_client_legacy::tangle::SharedPtr, - { - account: &'account mut Account, - $( - $field: Option<$ty>, - )* - } - - impl<'account, C> [<$ident Builder>]<'account, C> - where - C: identity_iota_client_legacy::tangle::SharedPtr, - { - $( - #[must_use] - pub fn $field>(mut self, value: VALUE) -> Self { - self.$field = Some(value.into()); - self - } - )* - - pub fn new(account: &'account mut Account) -> [<$ident Builder>]<'account, C> - where - C: identity_iota_client_legacy::tangle::SharedPtr, - { - [<$ident Builder>] { - account, - $( - $field: None, - )* - } - } - - pub async fn apply(self) -> $crate::Result<()> { - let update = $crate::updates::Update::$ident { - $( - $field: impl_update_builder!(@finish self $requirement $field $ty $(= $value)?), - )* - }; - - self.account.process_update(update).await - } - } - - impl<'account, C> $crate::types::IdentityUpdater<'account, C> - where - C: identity_iota_client_legacy::tangle::SharedPtr, - { - /// Creates a new builder to modify the identity. See the documentation of the return type for details. - pub fn [<$ident:snake>](&'account mut self) -> [<$ident Builder>]<'account, C> { - [<$ident Builder>]::new(self.account) - } - } - } - }; -} diff --git a/identity_account/src/updates/mod.rs b/identity_account/src/updates/mod.rs deleted file mode 100644 index d1ed6a6881..0000000000 --- a/identity_account/src/updates/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use self::error::*; -pub use self::update::*; - -#[macro_use] -mod macros; - -mod error; -mod update; diff --git a/identity_account/src/updates/update.rs b/identity_account/src/updates/update.rs deleted file mode 100644 index f3000bc8e6..0000000000 --- a/identity_account/src/updates/update.rs +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_account_storage::types::DIDType; -use identity_did::did::CoreDID; -use log::debug; -use log::trace; - -use identity_account_storage::storage::Storage; -use identity_account_storage::types::KeyLocation; -use identity_core::common::Fragment; -use identity_core::common::Object; -use identity_core::common::OneOrSet; -use identity_core::common::OrderedSet; -use identity_core::common::Timestamp; -use identity_core::common::Url; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_core::crypto::PrivateKey; -use identity_core::crypto::PublicKey; -use identity_did::did::DID; -use identity_did::service::Service; -use identity_did::service::ServiceEndpoint; -use identity_did::utils::Queryable; -use identity_did::verification::MethodRef; -use identity_did::verification::MethodRelationship; -use identity_did::verification::MethodScope; -use identity_iota_client_legacy::tangle::Client; -use identity_iota_client_legacy::tangle::SharedPtr; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::did::IotaDIDUrl; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::document::IotaService; -use identity_iota_core_legacy::document::IotaVerificationMethod; -use identity_iota_core_legacy::tangle::NetworkName; - -use crate::account::Account; -use crate::error::Result; -use crate::types::IdentitySetup; -use crate::types::MethodContent; -use crate::updates::UpdateError; - -pub(crate) async fn create_identity( - setup: IdentitySetup, - network: NetworkName, - store: &dyn Storage, -) -> Result { - let fragment: &str = IotaDocument::DEFAULT_METHOD_FRAGMENT; - - if let Some(private_key) = &setup.private_key { - KeyPair::try_from_private_key_bytes(KeyType::Ed25519, private_key.as_ref()) - .map_err(|err| UpdateError::InvalidMethodContent(err.to_string()))?; - }; - - let (did, location) = store - .did_create(DIDType::IotaDID, network.clone(), fragment, setup.private_key) - .await?; - - let public_key: PublicKey = store.key_public(&did, &location).await?; - - let method: IotaVerificationMethod = - IotaVerificationMethod::new(did.clone().try_into()?, KeyType::Ed25519, &public_key, fragment)?; - - let document = IotaDocument::from_verification_method(method)?; - - Ok(document) -} - -#[derive(Clone, Debug)] -pub(crate) enum Update { - CreateMethod { - scope: MethodScope, - content: MethodContent, - fragment: String, - }, - DeleteMethod { - fragment: String, - }, - AttachMethodRelationship { - fragment: String, - relationships: Vec, - }, - DetachMethodRelationship { - fragment: String, - relationships: Vec, - }, - CreateService { - fragment: String, - types: Vec, - endpoint: ServiceEndpoint, - properties: Option, - }, - DeleteService { - fragment: String, - }, - SetController { - controllers: Option>, - }, - SetAlsoKnownAs { - urls: OrderedSet, - }, -} - -impl Update { - pub(crate) async fn process(self, did: &IotaDID, document: &mut IotaDocument, storage: &dyn Storage) -> Result<()> { - debug!("[Update::process] Update = {:?}", self); - trace!("[Update::process] Document = {:?}", document); - trace!("[Update::process] Store = {:?}", storage); - - match self { - Self::CreateMethod { - scope, - content, - fragment, - } => { - let fragment: Fragment = Fragment::new(fragment); - - // Check method identifier is not duplicated. - let method_url: IotaDIDUrl = did.to_url().join(fragment.identifier())?; - if document.resolve_method(method_url, None).is_some() { - return Err(crate::Error::DIDError(identity_did::Error::MethodInsertionError)); - } - - // Generate or extract the private key and/or retrieve the public key. - let key_type: KeyType = content.key_type(); - let public: PublicKey = match content { - MethodContent::GenerateEd25519 | MethodContent::GenerateX25519 => { - let location: KeyLocation = storage.key_generate(did.as_ref(), key_type, fragment.name()).await?; - storage.key_public(did.as_ref(), &location).await? - } - MethodContent::PrivateEd25519(private_key) | MethodContent::PrivateX25519(private_key) => { - let location: KeyLocation = - insert_method_secret(storage, did.as_ref(), key_type, fragment.name(), private_key).await?; - storage.key_public(did.as_ref(), &location).await? - } - MethodContent::PublicEd25519(public_key) => public_key, - MethodContent::PublicX25519(public_key) => public_key, - }; - - // Insert a new method. - let method: IotaVerificationMethod = - IotaVerificationMethod::new(did.clone(), key_type, &public, fragment.name())?; - - document.insert_method(method, scope)?; - } - Self::DeleteMethod { fragment } => { - let fragment: Fragment = Fragment::new(fragment); - - let method_url: IotaDIDUrl = did.to_url().join(fragment.identifier())?; - - // Prevent deleting the last method capable of signing the DID document. - let capability_invocation_set = document.core_document().capability_invocation(); - let is_capability_invocation = capability_invocation_set - .iter() - .any(|method_ref| method_ref.id() == &method_url); - - ensure!( - !(is_capability_invocation && capability_invocation_set.len() == 1), - UpdateError::InvalidMethodFragment("cannot remove last signing method") - ); - - document.remove_method(&method_url)?; - } - Self::AttachMethodRelationship { - fragment, - relationships, - } => { - let fragment: Fragment = Fragment::new(fragment); - - let method_url: IotaDIDUrl = did.to_url().join(fragment.identifier())?; - - for relationship in relationships { - // Ignore result: attaching is idempotent. - let _ = document.attach_method_relationship(&method_url, relationship)?; - } - } - Self::DetachMethodRelationship { - fragment, - relationships, - } => { - let fragment: Fragment = Fragment::new(fragment); - - let method_url: IotaDIDUrl = did.to_url().join(fragment.identifier())?; - - // Prevent detaching the last method capable of signing the DID document. - let capability_invocation_set: &OrderedSet> = - document.core_document().capability_invocation(); - let is_capability_invocation = capability_invocation_set - .iter() - .any(|method_ref| method_ref.id() == &method_url); - - ensure!( - !(is_capability_invocation && capability_invocation_set.len() == 1), - UpdateError::InvalidMethodFragment("cannot remove last signing method") - ); - - for relationship in relationships { - // Ignore result: detaching is idempotent. - let _ = document.detach_method_relationship(&method_url, relationship)?; - } - } - Self::CreateService { - fragment, - types, - endpoint, - properties, - } => { - let fragment = Fragment::new(fragment); - let did_url: IotaDIDUrl = did.to_url().join(fragment.identifier())?; - - // The service must not exist. - ensure!( - document.service().query(&did_url).is_none(), - UpdateError::DuplicateServiceFragment(fragment.name().to_owned()), - ); - - let service: IotaService = Service::builder(properties.unwrap_or_default()) - .id(did_url) - .service_endpoint(endpoint) - .types(types) - .build()?; - - document.insert_service(service); - } - Self::DeleteService { fragment } => { - let fragment: Fragment = Fragment::new(fragment); - let service_url: IotaDIDUrl = did.to_url().join(fragment.identifier())?; - - // The service must exist - ensure!( - document.service().query(&service_url).is_some(), - UpdateError::ServiceNotFound - ); - - document.remove_service(&service_url); - } - Self::SetController { controllers } => { - *document.controller_mut() = controllers; - } - - Self::SetAlsoKnownAs { urls } => { - *document.also_known_as_mut() = urls; - } - } - - document.metadata.updated = Some(Timestamp::now_utc()); - - Ok(()) - } -} - -async fn insert_method_secret( - store: &dyn Storage, - did: &CoreDID, - key_type: KeyType, - fragment: &str, - private_key: PrivateKey, -) -> Result { - let keypair: KeyPair = KeyPair::try_from_private_key_bytes(key_type, private_key.as_ref()) - .map_err(|err| UpdateError::InvalidMethodContent(err.to_string()))?; - - let location: KeyLocation = KeyLocation::new(key_type, fragment.to_owned(), keypair.public().as_ref()); - std::mem::drop(keypair); - - ensure!( - !store.key_exists(did, &location).await?, - UpdateError::DuplicateKeyLocation(location) - ); - - store.key_insert(did, &location, private_key).await?; - - Ok(location) -} - -// ============================================================================= - -// ============================================================================= -// Update Builders -impl_update_builder!( -/// Create a new method on an identity. -/// -/// # Parameters -/// - `scope`: the scope of the method, defaults to [`MethodScope::default`]. -/// - `fragment`: the identifier of the method in the document, required. -/// - `content`: the key material to use for the method or key type to generate. -CreateMethod { - @default scope MethodScope, - @required fragment String, - @required content MethodContent, -}); - -impl_update_builder!( -/// Delete a method on an identity. -/// -/// # Parameters -/// - `fragment`: the identifier of the method in the document, required. -DeleteMethod { - @required fragment String, -}); - -impl_update_builder!( -/// Attach one or more verification relationships to a method on an identity. -/// -/// # Parameters -/// - `relationships`: the relationships to add, defaults to an empty [`Vec`]. -/// - `fragment`: the identifier of the method in the document, required. -AttachMethodRelationship { - @required fragment String, - @default relationships Vec, -}); - -impl<'account, C> AttachMethodRelationshipBuilder<'account, C> -where - C: SharedPtr, -{ - #[must_use] - pub fn relationship(mut self, value: MethodRelationship) -> Self { - self.relationships.get_or_insert_with(Default::default).push(value); - self - } -} - -impl_update_builder!( -/// Detaches one or more verification relationships from a method on an identity. -/// -/// # Parameters -/// - `relationships`: the relationships to remove, defaults to an empty [`Vec`]. -/// - `fragment`: the identifier of the method in the document, required. -DetachMethodRelationship { - @required fragment String, - @default relationships Vec, -}); - -impl<'account, C> DetachMethodRelationshipBuilder<'account, C> -where - C: SharedPtr, -{ - #[must_use] - pub fn relationship(mut self, value: MethodRelationship) -> Self { - self.relationships.get_or_insert_with(Default::default).push(value); - self - } -} - -impl_update_builder!( -/// Create a new service on an identity. -/// -/// # Parameters -/// - `types`: the type/s of the service, e.g. `"LinkedDomains"`, required. -/// - `fragment`: the identifier of the service in the document, required. -/// - `endpoint`: the `ServiceEndpoint` of the service, required. -/// - `properties`: additional properties of the service, optional. -CreateService { - @required fragment String, - @required types Vec, - @required endpoint ServiceEndpoint, - @optional properties Object, -}); - -impl<'account, C> CreateServiceBuilder<'account, C> -where - C: SharedPtr, -{ - #[must_use] - pub fn type_(mut self, value: impl Into) -> Self { - self.types.get_or_insert_with(Default::default).push(value.into()); - self - } -} - -impl_update_builder!( -/// Delete a service on an identity. -/// -/// # Parameters -/// - `fragment`: the identifier of the service in the document, required. -DeleteService { - @required fragment String, -}); - -impl_update_builder!( -SetController { - @required controllers Option>, -}); - -impl_update_builder!( -SetAlsoKnownAs { - @required urls OrderedSet, -}); diff --git a/identity_account_storage/Cargo.toml b/identity_account_storage/Cargo.toml deleted file mode 100644 index da7e08fb5e..0000000000 --- a/identity_account_storage/Cargo.toml +++ /dev/null @@ -1,53 +0,0 @@ -[package] -name = "identity_account_storage" -version = "0.7.0-alpha.1" -authors = ["IOTA Stiftung"] -edition = "2021" -homepage = "https://www.iota.org" -keywords = ["iota", "tangle", "identity", "storage"] -license = "Apache-2.0" -publish = false -readme = "./README.md" -repository = "https://github.com/iotaledger/identity.rs" -description = "Secure storage for Decentralized Identifiers and Verifiable Credentials." - -[workspace] - -[dependencies] -anyhow = { version = "1.0", default-features = false, features = ["std"], optional = true } -async-trait = { version = "0.1", default-features = false } -function_name = { version = "0.2", default-features = false, optional = true } -futures = { version = "0.3", optional = true } -hashbrown = { version = "0.11", features = ["serde"] } -identity_core = { version = "0.7.0-alpha.1", path = "../identity_core", default-features = false } -identity_did = { version = "0.7.0-alpha.1", path = "../identity_did", default-features = false } -identity_iota_core_legacy = { version = "0.7.0-alpha.1", path = "../identity_iota_core_legacy", default-features = false } -iota-crypto = { version = "0.12.1", default-features = false, features = ["hmac", "pbkdf", "sha", "std", "aes-gcm", "aes-kw"] } -iota_stronghold = { version = "0.6.4", default-features = false, features = ["std"], optional = true } -once_cell = { version = "1.7", default-features = false, features = ["std"], optional = true } -rand = { version = "0.8", default-features = false, features = ["std", "std_rng"], optional = true } -seahash = { version = "4.1.0", default-features = false } -serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } -strum = { version = "0.24.0", default-features = false, features = ["std", "derive"] } -thiserror = { version = "1.0" } -tokio = { version = "1.17.0", default-features = false, features = ["sync", "fs"], optional = true } -zeroize = { version = "1.4" } - -[dev-dependencies] -rusty-fork = { version = "0.3" } -tokio = { version = "1.17.0", default-features = false, features = ["macros", "rt", "rt-multi-thread", "sync"] } - -[features] -default = [ - "stronghold", - "send-sync-storage", - "storage-test-suite", - "encryption", -] -stronghold = ["iota_stronghold", "tokio", "futures", "once_cell", "rand"] -# Enables `Send` + `Sync` bounds for the Storage trait. -send-sync-storage = [] -# Exposes Storage `test_suite` module. -storage-test-suite = ["anyhow", "function_name", "rand"] -# Enables encryption and decryption in the Storage trait. -encryption = [] diff --git a/identity_account_storage/README.md b/identity_account_storage/README.md deleted file mode 100644 index aed78700a0..0000000000 --- a/identity_account_storage/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# IOTA Identity - Account Storage - -This crates defines the [`Storage`](crate::storage::Storage) trait which can be implemented for secure cryptographic operations, such as key generation and signing, as well as key-value like storage of data structures, such as DID Documents. - -## Implementations - -- [`Stronghold`](crate::storage::Stronghold) implements [`Storage`](crate::storage::Storage) and provides secure data storage and cryptographic operations using [IOTA Stronghold](https://github.com/iotaledger/stronghold.rs). -- [`MemStore`](crate::storage::MemStore) is an in-memory [`Storage`](crate::storage::Storage). It serves as an example implementation for reference and local testing, it is not intended for use in production! - -## Test Suite -[`StorageTestSuite`](crate::storage::StorageTestSuite) helps with testing [`Storage`](crate::storage::Storage) implementations. \ No newline at end of file diff --git a/identity_account_storage/src/crypto/mod.rs b/identity_account_storage/src/crypto/mod.rs deleted file mode 100644 index a08fb953c6..0000000000 --- a/identity_account_storage/src/crypto/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod remote; - -pub use self::remote::*; diff --git a/identity_account_storage/src/crypto/remote.rs b/identity_account_storage/src/crypto/remote.rs deleted file mode 100644 index 24896706b0..0000000000 --- a/identity_account_storage/src/crypto/remote.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::marker::PhantomData; - -use identity_did::did::CoreDID; -use serde::Serialize; - -use identity_core::convert::ToJson; -use identity_core::crypto::Named; -use identity_core::crypto::Proof; -use identity_core::crypto::ProofOptions; -use identity_core::crypto::ProofValue; -use identity_core::crypto::SetSignature; -use identity_core::error::Error; -use identity_core::error::Result; -use identity_core::utils::BaseEncoding; - -use crate::storage::Storage; -use crate::types::KeyLocation; -use crate::types::Signature as StorageSignature; - -pub struct RemoteEd25519; - -impl Named for RemoteEd25519 { - const NAME: &'static str = "JcsEd25519Signature2020"; -} - -impl RemoteEd25519 { - pub async fn create_signature( - data: &mut U, - method: impl Into, - secret: &RemoteKey<'_>, - options: ProofOptions, - ) -> Result<()> - where - U: Serialize + SetSignature, - { - let signature: Proof = Proof::new_with_options(Self::NAME, method, options); - data.set_signature(signature); - - let value: ProofValue = Self::sign(&data, secret).await?; - let write: &mut Proof = data.signature_mut().ok_or(Error::MissingSignature)?; - - write.set_value(value); - - Ok(()) - } - - pub async fn sign(data: &X, remote_key: &RemoteKey<'_>) -> Result - where - X: Serialize, - { - let message: Vec = data.to_jcs()?; - let signature: Vec = RemoteSign::sign(&message, remote_key).await?.into(); - let encoded: String = BaseEncoding::encode_base58(&signature); - Ok(ProofValue::Signature(encoded)) - } -} - -/// A reference to a storage instance and identity key location. -#[derive(Debug)] -pub struct RemoteKey<'a> { - did: &'a CoreDID, - location: &'a KeyLocation, - store: &'a dyn Storage, -} - -impl<'a> RemoteKey<'a> { - /// Creates a new `RemoteKey` instance. - pub fn new(did: &'a CoreDID, location: &'a KeyLocation, store: &'a dyn Storage) -> Self { - Self { did, location, store } - } -} - -// ============================================================================= -// RemoteSign -// ============================================================================= - -/// A remote signature that delegates to a storage implementation. -/// -/// Note: The signature implementation is specified by the associated `RemoteKey`. -#[derive(Clone, Copy, Debug)] -pub struct RemoteSign<'a> { - marker: PhantomData>, -} - -impl<'a> RemoteSign<'a> { - pub async fn sign(message: &[u8], key: &RemoteKey<'a>) -> Result { - key - .store - .key_sign(key.did, key.location, message.to_vec()) - .await - .map_err(|_| Error::InvalidProofValue("remote sign")) - } -} diff --git a/identity_account_storage/src/error.rs b/identity_account_storage/src/error.rs deleted file mode 100644 index 43f33a7ac3..0000000000 --- a/identity_account_storage/src/error.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -//! Errors that may occur when working with Identity Accounts. - -/// Alias for a `Result` with the error type [`Error`]. -pub type Result = ::core::result::Result; - -/// This type represents all possible errors that can occur in the library. -#[derive(Debug, thiserror::Error, strum::IntoStaticStr)] -pub enum Error { - /// Caused by errors from the [identity_core] crate. - #[error(transparent)] - CoreError(#[from] identity_core::Error), - /// Caused by errors from the [`identity_iota_core_legacy`] crate. - #[error("DID creation failed: {0}")] - DIDCreationError(String), - /// Caused by errors from the [identity_did] crate. - #[error(transparent)] - DIDError(#[from] identity_did::Error), - /// Caused by attempting to perform an invalid IO operation. - #[error(transparent)] - IoError(#[from] std::io::Error), - /// Caused by errors from the [iota_stronghold] crate. - #[cfg(feature = "stronghold")] - #[error(transparent)] - StrongholdError(#[from] crate::stronghold::StrongholdError), - /// Caused by providing bytes that cannot be used as a private key of the - /// [`KeyType`][identity_core::crypto::KeyType]. - #[error("invalid private key: {0}")] - InvalidPrivateKey(String), - /// Caused by providing bytes that cannot be used as a public key of the - /// [`KeyType`][identity_core::crypto::KeyType]. - #[error("invalid public key: {0}")] - InvalidPublicKey(String), - /// Caused by failing to decrypt data. - #[error("failed to decrypt data")] - DecryptionFailure(#[source] crypto::error::Error), - /// Caused by failing to encrypt data. - #[error("failed to encrypt data")] - EncryptionFailure(#[source] crypto::error::Error), - /// Caused by attempting to find a key in storage that does not exist. - #[error("key not found")] - KeyNotFound, - /// Caused by attempting to find an identity key vault that does not exist. - #[error("key vault not found")] - KeyVaultNotFound, - /// Caused by attempting to read a poisoned shared resource. - #[error("shared resource poisoned: read")] - SharedReadPoisoned, - /// Caused by attempting to write a poisoned shared resource. - #[error("shared resource poisoned: write")] - SharedWritePoisoned, - /// Caused by attempting to create a DID that already exists. - #[error("identity already exists")] - IdentityAlreadyExists, - #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] - #[error("JsValue serialization error: {0}")] - SerializationError(String), - #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] - #[error("javascript function threw an exception: {0}")] - JsError(String), -} - -impl From for Error { - fn from(error: identity_did::did::DIDError) -> Self { - identity_did::Error::from(error).into() - } -} diff --git a/identity_account_storage/src/identity/chain_state.rs b/identity_account_storage/src/identity/chain_state.rs deleted file mode 100644 index a7415aa344..0000000000 --- a/identity_account_storage/src/identity/chain_state.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota_core_legacy::tangle::MessageId; -use identity_iota_core_legacy::tangle::MessageIdExt; -use serde::Deserialize; -use serde::Serialize; - -/// Holds the last published message ids of the integration and diff chains. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct ChainState { - #[serde(default = "MessageId::null", skip_serializing_if = "MessageId::is_null")] - last_integration_message_id: MessageId, - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] - #[serde(default = "MessageId::null", skip_serializing_if = "MessageId::is_null")] - last_diff_message_id: MessageId, -} - -impl ChainState { - pub fn new() -> Self { - Self { - last_integration_message_id: MessageId::null(), - last_diff_message_id: MessageId::null(), - } - } - - /// Returns the integration message id of the last published update. - /// - /// Note: [`MessageId`] has a built-in `null` variant that needs to be checked for. - pub fn last_integration_message_id(&self) -> &MessageId { - &self.last_integration_message_id - } - - /// Returns the diff message id of the last published update. - /// - /// Note: [`MessageId`] has a built-in `null` variant that needs to be checked for. - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] - pub fn last_diff_message_id(&self) -> &MessageId { - &self.last_diff_message_id - } - - /// Sets the last integration message id and resets the - /// last diff message id to [`MessageId::null()`]. - pub fn set_last_integration_message_id(&mut self, message: MessageId) { - self.last_integration_message_id = message; - - // Clear the diff message id - self.last_diff_message_id = MessageId::null(); - } - - /// Sets the last diff message id. - #[deprecated(since = "0.5.0", note = "diff chain features are slated for removal")] - pub fn set_last_diff_message_id(&mut self, message: MessageId) { - self.last_diff_message_id = message; - } - - /// Returns whether the identity has been published before. - pub fn is_new_identity(&self) -> bool { - self.last_integration_message_id.is_null() - } -} - -impl Default for ChainState { - fn default() -> Self { - Self::new() - } -} diff --git a/identity_account_storage/src/identity/mod.rs b/identity_account_storage/src/identity/mod.rs deleted file mode 100644 index b3a4248a32..0000000000 --- a/identity_account_storage/src/identity/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod chain_state; - -pub use self::chain_state::*; diff --git a/identity_account_storage/src/lib.rs b/identity_account_storage/src/lib.rs deleted file mode 100644 index e9a6ddae88..0000000000 --- a/identity_account_storage/src/lib.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] -#![allow(deprecated)] -#![allow(clippy::upper_case_acronyms)] -#![doc = include_str!("./../README.md")] -#![warn( - rust_2018_idioms, - unreachable_pub, - // missing_docs, - // rustdoc::missing_crate_level_docs, - rustdoc::broken_intra_doc_links, - rustdoc::private_intra_doc_links, - rustdoc::private_doc_tests, - clippy::missing_safety_doc, - // clippy::missing_errors_doc -)] - -pub mod crypto; -pub mod error; -pub mod identity; -pub mod storage; -#[cfg(feature = "stronghold")] -pub mod stronghold; -pub mod types; -pub mod utils; - -pub use self::error::Error; -pub use self::error::Result; diff --git a/identity_account_storage/src/storage/memstore.rs b/identity_account_storage/src/storage/memstore.rs deleted file mode 100644 index 530a74a7c4..0000000000 --- a/identity_account_storage/src/storage/memstore.rs +++ /dev/null @@ -1,602 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Debug; -use core::fmt::Formatter; - -use async_trait::async_trait; -#[cfg(feature = "encryption")] -use crypto::ciphers::aes_gcm::Aes256Gcm; -#[cfg(feature = "encryption")] -use crypto::ciphers::aes_kw::Aes256Kw; -#[cfg(feature = "encryption")] -use crypto::ciphers::traits::Aead; -use hashbrown::HashMap; -use identity_core::crypto::Ed25519; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_core::crypto::PrivateKey; -use identity_core::crypto::PublicKey; -use identity_core::crypto::Sign; -#[cfg(feature = "encryption")] -use identity_core::crypto::X25519; -use identity_did::did::CoreDID; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::tangle::NetworkName; -use std::sync::RwLockReadGuard; -use std::sync::RwLockWriteGuard; -use zeroize::Zeroize; - -use crate::error::Error; -use crate::error::Result; -use crate::storage::Storage; -#[cfg(feature = "encryption")] -use crate::types::CekAlgorithm; -use crate::types::DIDType; -#[cfg(feature = "encryption")] -use crate::types::EncryptedData; -#[cfg(feature = "encryption")] -use crate::types::EncryptionAlgorithm; -use crate::types::KeyLocation; -use crate::types::Signature; -use crate::utils::Shared; - -// The map from DIDs to vaults. -type Vaults = HashMap; -// The map from key locations to key pairs, that lives within a DID partition. -type MemVault = HashMap; - -/// An insecure, in-memory [`Storage`] implementation that serves as an example and is used in tests. -pub struct MemStore { - // Controls whether to print the storages content when debugging. - expand: bool, - blobs: Shared>>, - vaults: Shared, -} - -impl MemStore { - /// Creates a new, empty `MemStore` instance. - pub fn new() -> Self { - Self { - expand: false, - blobs: Shared::new(HashMap::new()), - vaults: Shared::new(HashMap::new()), - } - } - - /// Returns whether to expand the debug representation. - pub fn expand(&self) -> bool { - self.expand - } - - /// Sets whether to expand the debug representation. - pub fn set_expand(&mut self, value: bool) { - self.expand = value; - } -} - -// Refer to the `Storage` interface docs for high-level documentation of the individual methods. -#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))] -#[cfg_attr(feature = "send-sync-storage", async_trait)] -impl Storage for MemStore { - async fn did_create( - &self, - did_type: DIDType, - network: NetworkName, - fragment: &str, - private_key: Option, - ) -> Result<(CoreDID, KeyLocation)> { - // Extract a `KeyPair` from the passed private key or generate a new one. - // For `did_create` we can assume the `KeyType` to be `Ed25519` because - // that is the only currently available signature type. - let keypair: KeyPair = match private_key { - Some(private_key) => KeyPair::try_from_private_key_bytes(KeyType::Ed25519, private_key.as_ref())?, - None => KeyPair::new(KeyType::Ed25519)?, - }; - - // We create the location at which the key pair will be stored. - // Most notably, this uses the public key as an input. - let location: KeyLocation = KeyLocation::new(KeyType::Ed25519, fragment.to_owned(), keypair.public().as_ref()); - - // Next we use the public key to derive the initial DID. - let did: CoreDID = { - match did_type { - DIDType::IotaDID => IotaDID::new_with_network(keypair.public().as_ref(), network) - .map_err(|err| crate::Error::DIDCreationError(err.to_string()))? - .into(), - } - }; - - // Obtain exclusive access to the vaults. - let mut vaults: RwLockWriteGuard<'_, _> = self.vaults.write()?; - - // We use the vaults as the index of DIDs stored in this storage instance. - // If the DID already exists, we need to return an error. We don't want to overwrite an existing DID. - if vaults.contains_key(&did) { - return Err(Error::IdentityAlreadyExists); - } - - // Obtain the exiting mem vault or create a new one. - let vault: &mut MemVault = vaults.entry(did.clone()).or_default(); - - // Insert the key pair at the previously created location. - vault.insert(location.clone(), keypair); - - // Return did and location. - Ok((did, location)) - } - - async fn did_purge(&self, did: &CoreDID) -> Result { - // This method is supposed to be idempotent, - // so we only need to do work if the DID still exists. - // The return value signals whether the DID was actually removed during this operation. - if self.vaults.write()?.remove(did).is_some() { - let _ = self.blobs.write()?.remove(did); - Ok(true) - } else { - Ok(false) - } - } - - async fn did_exists(&self, did: &CoreDID) -> Result { - // Note that any failure to get access to the storage and do the actual existence check - // should result in an error rather than returning `false`. - Ok(self.vaults.read()?.contains_key(did)) - } - - async fn did_list(&self) -> Result> { - Ok(self.vaults.read()?.keys().cloned().collect()) - } - - async fn key_generate(&self, did: &CoreDID, key_type: KeyType, fragment: &str) -> Result { - // Obtain exclusive access to the vaults. - let mut vaults: RwLockWriteGuard<'_, _> = self.vaults.write()?; - // Get or insert the MemVault. - let vault: &mut MemVault = vaults.entry(did.clone()).or_default(); - - // Generate a new key pair for the given `key_type`. - let keypair: KeyPair = KeyPair::new(key_type)?; - - // Derive the key location from the fragment and public key and set the `KeyType` of the location. - let location: KeyLocation = KeyLocation::new(key_type, fragment.to_owned(), keypair.public().as_ref()); - - vault.insert(location.clone(), keypair); - - // Return the location at which the key was generated. - Ok(location) - } - - async fn key_insert(&self, did: &CoreDID, location: &KeyLocation, mut private_key: PrivateKey) -> Result<()> { - // Obtain exclusive access to the vaults. - let mut vaults: RwLockWriteGuard<'_, _> = self.vaults.write()?; - // Get or insert the MemVault. - let vault: &mut MemVault = vaults.entry(did.clone()).or_default(); - - // Reconstruct the key pair from the given private key by inspecting the location for its key type. - // Then insert the key at the given location. - match location.key_type { - KeyType::Ed25519 => { - let keypair: KeyPair = KeyPair::try_from_private_key_bytes(KeyType::Ed25519, private_key.as_ref()) - .map_err(|err| Error::InvalidPrivateKey(err.to_string()))?; - private_key.zeroize(); - - vault.insert(location.to_owned(), keypair); - - Ok(()) - } - KeyType::X25519 => { - let keypair: KeyPair = KeyPair::try_from_private_key_bytes(KeyType::X25519, private_key.as_ref()) - .map_err(|err| Error::InvalidPrivateKey(err.to_string()))?; - private_key.zeroize(); - - vault.insert(location.to_owned(), keypair); - - Ok(()) - } - } - } - - async fn key_exists(&self, did: &CoreDID, location: &KeyLocation) -> Result { - // Obtain read access to the vaults. - let vaults: RwLockReadGuard<'_, _> = self.vaults.read()?; - - // Within the DID vault, check for existence of the given location. - if let Some(vault) = vaults.get(did) { - return Ok(vault.contains_key(location)); - } - - Ok(false) - } - - async fn key_public(&self, did: &CoreDID, location: &KeyLocation) -> Result { - // Obtain read access to the vaults. - let vaults: RwLockReadGuard<'_, _> = self.vaults.read()?; - // Lookup the vault for the given DID. - let vault: &MemVault = vaults.get(did).ok_or(Error::KeyVaultNotFound)?; - // Lookup the key pair within the vault. - let keypair: &KeyPair = vault.get(location).ok_or(Error::KeyNotFound)?; - - // Return the public key. - Ok(keypair.public().clone()) - } - - async fn key_delete(&self, did: &CoreDID, location: &KeyLocation) -> Result { - // Obtain read access to the vaults. - let mut vaults: RwLockWriteGuard<'_, _> = self.vaults.write()?; - // Lookup the vault for the given DID. - let vault: &mut MemVault = vaults.get_mut(did).ok_or(Error::KeyVaultNotFound)?; - - // This method is supposed to be idempotent, so we delete the key - // if it exists and return whether it was actually deleted during this operation. - Ok(vault.remove(location).is_some()) - } - - async fn key_sign(&self, did: &CoreDID, location: &KeyLocation, data: Vec) -> Result { - // Obtain read access to the vaults. - let vaults: RwLockReadGuard<'_, _> = self.vaults.read()?; - // Lookup the vault for the given DID. - let vault: &MemVault = vaults.get(did).ok_or(Error::KeyVaultNotFound)?; - // Lookup the key pair within the vault. - let keypair: &KeyPair = vault.get(location).ok_or(Error::KeyNotFound)?; - - match location.key_type { - KeyType::Ed25519 => { - assert_eq!(keypair.type_(), KeyType::Ed25519); - - // Use the `Ed25519` API to sign the given data with the private key. - let signature: [u8; 64] = Ed25519::sign(&data, keypair.private())?; - // Construct a new `Signature` wrapper with the returned signature bytes. - let signature: Signature = Signature::new(signature.to_vec()); - Ok(signature) - } - KeyType::X25519 => { - // Calling key_sign on key types that cannot be signed with should return an error. - return Err(identity_did::Error::InvalidMethodType.into()); - } - } - } - - #[cfg(feature = "encryption")] - async fn data_encrypt( - &self, - _did: &CoreDID, - plaintext: Vec, - associated_data: Vec, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - public_key: PublicKey, - ) -> Result { - let public_key: [u8; X25519::PUBLIC_KEY_LENGTH] = public_key - .as_ref() - .try_into() - .map_err(|_| Error::InvalidPublicKey(format!("expected public key of length {}", X25519::PUBLIC_KEY_LENGTH)))?; - match cek_algorithm { - CekAlgorithm::ECDH_ES(agreement) => { - // Generate ephemeral key - let keypair: KeyPair = KeyPair::new(KeyType::X25519)?; - // Obtain the shared secret by combining the ephemeral key and the static public key - let shared_secret: [u8; 32] = X25519::key_exchange(keypair.private(), &public_key)?; - let derived_secret: Vec = - memstore_encryption::concat_kdf(cek_algorithm.name(), Aes256Gcm::KEY_LENGTH, &shared_secret, agreement) - .map_err(Error::EncryptionFailure)?; - let encrypted_data = memstore_encryption::try_encrypt( - &derived_secret, - encryption_algorithm, - &plaintext, - associated_data, - Vec::new(), - keypair.public().as_ref().to_vec(), - )?; - Ok(encrypted_data) - } - CekAlgorithm::ECDH_ES_A256KW(agreement) => { - let keypair: KeyPair = KeyPair::new(KeyType::X25519)?; - let shared_secret: [u8; 32] = X25519::key_exchange(keypair.private(), &public_key)?; - let derived_secret: Vec = - memstore_encryption::concat_kdf(cek_algorithm.name(), Aes256Kw::KEY_LENGTH, &shared_secret, agreement) - .map_err(Error::EncryptionFailure)?; - - let cek: Vec = memstore_encryption::generate_content_encryption_key(*encryption_algorithm)?; - - let mut encrypted_cek: Vec = vec![0; cek.len() + Aes256Kw::BLOCK]; - let aes_kw: Aes256Kw<'_> = Aes256Kw::new(derived_secret.as_ref()); - aes_kw - .wrap_key(cek.as_ref(), &mut encrypted_cek) - .map_err(Error::EncryptionFailure)?; - - let encrypted_data = memstore_encryption::try_encrypt( - &cek, - encryption_algorithm, - &plaintext, - associated_data, - encrypted_cek, - keypair.public().as_ref().to_vec(), - )?; - Ok(encrypted_data) - } - } - } - - #[cfg(feature = "encryption")] - async fn data_decrypt( - &self, - did: &CoreDID, - data: EncryptedData, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - private_key: &KeyLocation, - ) -> Result> { - // Retrieves the PrivateKey from the vault - let vaults: RwLockReadGuard<'_, _> = self.vaults.read()?; - let vault: &MemVault = vaults.get(did).ok_or(Error::KeyVaultNotFound)?; - let key_pair: &KeyPair = vault.get(private_key).ok_or(Error::KeyNotFound)?; - // Decrypts the data - match key_pair.type_() { - KeyType::Ed25519 => Err(Error::InvalidPrivateKey( - "Ed25519 keys are not supported for decryption".to_owned(), - )), - KeyType::X25519 => { - let public_key: [u8; X25519::PUBLIC_KEY_LENGTH] = - data.ephemeral_public_key.clone().try_into().map_err(|_| { - Error::InvalidPublicKey(format!("expected public key of length {}", X25519::PUBLIC_KEY_LENGTH)) - })?; - match cek_algorithm { - CekAlgorithm::ECDH_ES(agreement) => { - let shared_secret: [u8; 32] = X25519::key_exchange(key_pair.private(), &public_key)?; - let derived_secret: Vec = - memstore_encryption::concat_kdf(cek_algorithm.name(), Aes256Gcm::KEY_LENGTH, &shared_secret, agreement) - .map_err(Error::DecryptionFailure)?; - memstore_encryption::try_decrypt(&derived_secret, encryption_algorithm, &data) - } - CekAlgorithm::ECDH_ES_A256KW(agreement) => { - let shared_secret: [u8; 32] = X25519::key_exchange(key_pair.private(), &public_key)?; - let derived_secret: Vec = - memstore_encryption::concat_kdf(cek_algorithm.name(), Aes256Kw::KEY_LENGTH, &shared_secret, agreement) - .map_err(Error::DecryptionFailure)?; - - let cek_len: usize = - data - .encrypted_cek - .len() - .checked_sub(Aes256Kw::BLOCK) - .ok_or(Error::DecryptionFailure(crypto::Error::BufferSize { - name: "plaintext cek", - needs: Aes256Kw::BLOCK, - has: data.encrypted_cek.len(), - }))?; - - let mut cek: Vec = vec![0; cek_len]; - let aes_kw: Aes256Kw<'_> = Aes256Kw::new(derived_secret.as_ref()); - aes_kw - .unwrap_key(data.encrypted_cek.as_ref(), &mut cek) - .map_err(Error::DecryptionFailure)?; - - memstore_encryption::try_decrypt(&cek, encryption_algorithm, &data) - } - } - } - } - } - - async fn blob_set(&self, did: &CoreDID, value: Vec) -> Result<()> { - // Set the arbitrary value for the given DID. - self.blobs.write()?.insert(did.clone(), value); - - Ok(()) - } - - async fn blob_get(&self, did: &CoreDID) -> Result>> { - // Lookup the value stored of the given DID. - self.blobs.read().map(|data| data.get(did).cloned()) - } - - async fn flush_changes(&self) -> Result<()> { - // The MemStore doesn't need to flush changes to disk or any other persistent store, - // which is why this function does nothing. - Ok(()) - } -} - -#[cfg(feature = "encryption")] -mod memstore_encryption { - use crate::types::AgreementInfo; - use crate::types::EncryptedData; - use crate::types::EncryptionAlgorithm; - use crate::Error; - use crate::Result; - use crypto::ciphers::aes_gcm::Aes256Gcm; - use crypto::ciphers::traits::Aead; - use crypto::hashes::sha::Sha256; - use crypto::hashes::Digest; - - pub(crate) fn try_encrypt( - key: &[u8], - algorithm: &EncryptionAlgorithm, - data: &[u8], - associated_data: Vec, - encrypted_cek: Vec, - ephemeral_public_key: Vec, - ) -> Result { - match algorithm { - EncryptionAlgorithm::AES256GCM => { - let nonce: &[u8] = &Aes256Gcm::random_nonce().map_err(Error::EncryptionFailure)?; - let padding: usize = Aes256Gcm::padsize(data).map(|size| size.get()).unwrap_or_default(); - let mut ciphertext: Vec = vec![0; data.len() + padding]; - let mut tag: Vec = [0; Aes256Gcm::TAG_LENGTH].to_vec(); - Aes256Gcm::try_encrypt(key, nonce, associated_data.as_ref(), data, &mut ciphertext, &mut tag) - .map_err(Error::EncryptionFailure)?; - Ok(EncryptedData::new( - nonce.to_vec(), - associated_data, - tag, - ciphertext, - encrypted_cek, - ephemeral_public_key, - )) - } - } - } - - pub(crate) fn try_decrypt(key: &[u8], algorithm: &EncryptionAlgorithm, data: &EncryptedData) -> Result> { - match algorithm { - EncryptionAlgorithm::AES256GCM => { - let mut plaintext = vec![0; data.ciphertext.len()]; - let len: usize = Aes256Gcm::try_decrypt( - key, - &data.nonce, - &data.associated_data, - &mut plaintext, - &data.ciphertext, - &data.tag, - ) - .map_err(Error::DecryptionFailure)?; - plaintext.truncate(len); - Ok(plaintext) - } - } - } - - /// The Concat KDF (using SHA-256) as defined in Section 5.8.1 of NIST.800-56A - pub(crate) fn concat_kdf( - alg: &'static str, - len: usize, - shared_secret: &[u8], - agreement: &AgreementInfo, - ) -> crypto::error::Result> { - let mut digest: Sha256 = Sha256::new(); - let mut output: Vec = Vec::new(); - - let target: usize = (len + (Sha256::output_size() - 1)) / Sha256::output_size(); - let rounds: u32 = u32::try_from(target).map_err(|_| crypto::error::Error::InvalidArgumentError { - alg, - expected: "iterations can't exceed 2^32 - 1", - })?; - - for count in 0..rounds { - // Iteration Count - digest.update(&(count as u32 + 1).to_be_bytes()); - - // Derived Secret - digest.update(shared_secret); - - // AlgorithmId - digest.update(&(alg.len() as u32).to_be_bytes()); - digest.update(alg.as_bytes()); - - // PartyUInfo - digest.update(&(agreement.apu.len() as u32).to_be_bytes()); - digest.update(&agreement.apu); - - // PartyVInfo - digest.update(&(agreement.apv.len() as u32).to_be_bytes()); - digest.update(&agreement.apv); - - // SuppPubInfo - digest.update(&agreement.pub_info); - - // SuppPrivInfo - digest.update(&agreement.priv_info); - - output.extend_from_slice(&digest.finalize_reset()); - } - - output.truncate(len); - - Ok(output) - } - - /// Generate a random content encryption key of suitable length for `encryption_algorithm`. - pub(crate) fn generate_content_encryption_key(encryption_algorithm: EncryptionAlgorithm) -> Result> { - let mut bytes: Vec = vec![0; encryption_algorithm.key_length()]; - crypto::utils::rand::fill(bytes.as_mut()).map_err(Error::EncryptionFailure)?; - Ok(bytes) - } -} - -impl Debug for MemStore { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - if self.expand { - f.debug_struct("MemStore") - .field("blobs", &self.blobs) - .field("vaults", &self.vaults) - .finish() - } else { - f.write_str("MemStore") - } - } -} - -impl Default for MemStore { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -#[cfg(feature = "storage-test-suite")] -mod tests { - use crate::storage::Storage; - use crate::storage::StorageTestSuite; - - use super::MemStore; - - fn test_memstore() -> impl Storage { - MemStore::new() - } - - #[tokio::test] - async fn test_memstore_did_create_with_private_key() { - StorageTestSuite::did_create_private_key_test(test_memstore()) - .await - .unwrap() - } - - #[tokio::test] - async fn test_memstore_did_create_generate_key() { - StorageTestSuite::did_create_generate_key_test(test_memstore()) - .await - .unwrap() - } - - #[tokio::test] - async fn test_memstore_key_generate() { - StorageTestSuite::key_generate_test(test_memstore()).await.unwrap() - } - - #[tokio::test] - async fn test_memstore_key_delete() { - StorageTestSuite::key_delete_test(test_memstore()).await.unwrap() - } - - #[tokio::test] - async fn test_memstore_did_list() { - StorageTestSuite::did_list_test(test_memstore()).await.unwrap() - } - - #[tokio::test] - async fn test_memstore_key_insert() { - StorageTestSuite::key_insert_test(test_memstore()).await.unwrap() - } - - #[tokio::test] - async fn test_memstore_key_sign_ed25519() { - StorageTestSuite::key_sign_ed25519_test(test_memstore()).await.unwrap() - } - - #[tokio::test] - async fn test_memstore_key_value_store() { - StorageTestSuite::key_value_store_test(test_memstore()).await.unwrap() - } - - #[tokio::test] - async fn test_memstore_did_purge() { - StorageTestSuite::did_purge_test(test_memstore()).await.unwrap() - } - - #[tokio::test] - async fn test_memstore_encryption() { - StorageTestSuite::encryption_test(test_memstore(), test_memstore()) - .await - .unwrap() - } -} diff --git a/identity_account_storage/src/storage/mod.rs b/identity_account_storage/src/storage/mod.rs deleted file mode 100644 index acd20ffef7..0000000000 --- a/identity_account_storage/src/storage/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod memstore; -#[cfg(feature = "stronghold")] -pub(crate) mod stronghold; -#[cfg(feature = "storage-test-suite")] -mod test_suite; -mod traits; - -pub use self::memstore::*; -pub use self::traits::*; -#[cfg(feature = "stronghold")] -pub use crate::stronghold::Stronghold; -#[cfg(feature = "storage-test-suite")] -pub use test_suite::StorageTestSuite; diff --git a/identity_account_storage/src/storage/stronghold.rs b/identity_account_storage/src/storage/stronghold.rs deleted file mode 100644 index 3635a816f7..0000000000 --- a/identity_account_storage/src/storage/stronghold.rs +++ /dev/null @@ -1,731 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::BTreeSet; - -use async_trait::async_trait; -use crypto::ciphers::aes_gcm::Aes256Gcm; -use crypto::ciphers::traits::Aead; -use futures::executor; -use identity_core::convert::FromJson; -use identity_core::convert::ToJson; -use identity_core::crypto::KeyType; -use identity_core::crypto::PrivateKey; -use identity_core::crypto::PublicKey; -use identity_core::crypto::X25519; -use identity_did::did::CoreDID; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::tangle::NetworkName; -use iota_stronghold::procedures; -use iota_stronghold::procedures::ProcedureError; -use iota_stronghold::procedures::Sha2Hash; -use iota_stronghold::sync::MergePolicy; -use iota_stronghold::sync::SyncClientsConfig; -use iota_stronghold::Client; -use iota_stronghold::ClientVault; -use iota_stronghold::Location; -use iota_stronghold::Store; -use rand::distributions::DistString; -use tokio::sync::RwLockReadGuard; -use tokio::sync::RwLockWriteGuard; -use zeroize::Zeroize; - -use crate::error::Error; -use crate::error::Result; -use crate::storage::Storage; -use crate::stronghold::ClientOperation; -use crate::stronghold::ClientPath; -use crate::stronghold::StoreOperation; -use crate::stronghold::Stronghold; -use crate::stronghold::StrongholdError; -use crate::stronghold::VaultOperation; -use crate::types::AgreementInfo; -use crate::types::CekAlgorithm; -use crate::types::DIDType; -use crate::types::EncryptedData; -use crate::types::EncryptionAlgorithm; -use crate::types::KeyLocation; -use crate::types::Signature; - -// The name of the stronghold client used for indexing, which is global for a storage instance. -static INDEX_CLIENT_PATH: &str = "$index"; -// The key in the index store that contains the serialized index. -// This happens to be the same as the client path, but for explicitness we define them separately. -static INDEX_STORE_KEY: &str = INDEX_CLIENT_PATH; -static BLOB_STORE_KEY: &str = "$blob"; -// The static identifier for vaults inside clients. -static VAULT_PATH: &[u8; 6] = b"$vault"; - -#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))] -#[cfg_attr(feature = "send-sync-storage", async_trait)] -impl Storage for Stronghold { - async fn did_create( - &self, - did_type: DIDType, - network: NetworkName, - fragment: &str, - private_key: Option, - ) -> Result<(CoreDID, KeyLocation)> { - // ============================= - // KEY GENERATION/INSERTION - // ============================= - - let tmp_client: Client = Client::default(); - let tmp_location: KeyLocation = random_location(KeyType::Ed25519); - - match private_key { - Some(private_key) => { - insert_private_key(&tmp_client, private_key, &tmp_location)?; - } - None => { - generate_private_key(&tmp_client, &tmp_location)?; - } - } - - let public_key: PublicKey = retrieve_public_key(&tmp_client, &tmp_location)?; - - let did: CoreDID = { - match did_type { - DIDType::IotaDID => IotaDID::new_with_network(public_key.as_ref(), network) - .map_err(|err| crate::Error::DIDCreationError(err.to_string()))? - .into(), - } - }; - - // ============================= - // ADD DID TO INDEX - // ============================= - - let index_lock: RwLockWriteGuard<'_, _> = self.index_lock.write().await; - - let index_client_path: ClientPath = ClientPath::from(INDEX_CLIENT_PATH); - let index_client: Client = self.client(&index_client_path)?; - let index_store: Store = index_client.store(); - - let mut index: BTreeSet = get_index(&index_store)?; - - if index.contains(&did) { - return Err(crate::Error::IdentityAlreadyExists); - } else { - index.insert(did.clone()); - } - - set_index(&index_store, index)?; - - self - .stronghold - .write_client(index_client_path.as_ref()) - .map_err(|err| StrongholdError::Client(ClientOperation::Persist, index_client_path, err))?; - - // Explicitly release the lock early. - std::mem::drop(index_lock); - - // ============================= - // CLIENT SYNC & KEY MOVE - // ============================= - let location: KeyLocation = KeyLocation::new(KeyType::Ed25519, fragment.to_owned(), public_key.as_ref()); - - self.mutate_client(&did, |client| { - // Sync the vault identified by VAULT_PATH from the tmp client to the client identified by the DID. - let mut sync_config: SyncClientsConfig = SyncClientsConfig::new(MergePolicy::Replace); - sync_config.sync_selected_vaults(vec![VAULT_PATH]); - - client - .sync_with(&tmp_client, sync_config) - .map_err(|err| StrongholdError::Client(ClientOperation::Sync, ClientPath::from(&did), err))?; - std::mem::drop(tmp_client); - - // Within client, move the key from the tmp location to the expected location. - move_key(&client, &tmp_location, &location)?; - - Ok(()) - })?; - - Ok((did, location)) - } - - async fn did_purge(&self, did: &CoreDID) -> Result { - let index_lock: RwLockReadGuard<'_, _> = self.index_lock.read().await; - - let index_client_path: ClientPath = ClientPath::from(INDEX_CLIENT_PATH); - let index_client: Client = self.client(&index_client_path)?; - let index_store: Store = index_client.store(); - - let mut index: BTreeSet = get_index(&index_store)?; - - // Remove index entry if present. - if !index.remove(did) { - return Ok(false); - } - - set_index(&index_store, index)?; - - self - .stronghold - .write_client(index_client_path.as_ref()) - .map_err(|err| StrongholdError::Client(ClientOperation::Persist, index_client_path, err))?; - - // Explicitly release the lock early. - std::mem::drop(index_lock); - - // Delete the client from the snapshot, which removes the store and the vaults (= all keys). - let client_path: ClientPath = ClientPath::from(did); - let client: Client = self.client(&client_path)?; - self - .stronghold - .purge_client(client) - .map_err(|err| StrongholdError::Client(ClientOperation::Purge, client_path, err))?; - - Ok(true) - } - - async fn did_exists(&self, did: &CoreDID) -> Result { - let index_lock: RwLockReadGuard<'_, _> = self.index_lock.read().await; - - let client: Client = self.client(&ClientPath::from(INDEX_CLIENT_PATH))?; - let store: Store = client.store(); - - let dids: BTreeSet = get_index(&store)?; - - let has_did: bool = dids.contains(did); - - // Explicitly drop the lock so it's not considered unused. - std::mem::drop(index_lock); - - Ok(has_did) - } - - async fn did_list(&self) -> Result> { - let index_lock: RwLockReadGuard<'_, _> = self.index_lock.read().await; - - let client: Client = self.client(&ClientPath::from(INDEX_CLIENT_PATH))?; - let store: Store = client.store(); - - let dids: BTreeSet = get_index(&store)?; - - // Explicitly drop the lock so it's not considered unused. - std::mem::drop(index_lock); - - Ok(dids.into_iter().collect()) - } - - async fn key_generate(&self, did: &CoreDID, key_type: KeyType, fragment: &str) -> Result { - self.mutate_client(did, |client| { - let tmp_location: KeyLocation = random_location(key_type); - - match key_type { - KeyType::Ed25519 | KeyType::X25519 => { - generate_private_key(&client, &tmp_location)?; - } - } - - let public_key: PublicKey = retrieve_public_key(&client, &tmp_location)?; - let location: KeyLocation = KeyLocation::new(key_type, fragment.to_owned(), public_key.as_ref()); - - move_key(&client, &tmp_location, &location)?; - - Ok(location) - }) - } - - async fn key_insert(&self, did: &CoreDID, location: &KeyLocation, private_key: PrivateKey) -> Result<()> { - self.mutate_client(did, |client| insert_private_key(&client, private_key, location)) - } - - async fn key_public(&self, did: &CoreDID, location: &KeyLocation) -> Result { - let client: Client = self.client(&ClientPath::from(did))?; - retrieve_public_key(&client, location) - } - - async fn key_delete(&self, did: &CoreDID, location: &KeyLocation) -> Result { - self.mutate_client(did, |client| { - // Technically there is a race condition here between existence check and removal. - // However, the RevokeData procedure does not return an error if the record doesn't exist, so it's fine. - - let exists: bool = client - .record_exists(&location.into()) - .map_err(|err| StrongholdError::Vault(VaultOperation::RecordExists, err)) - .map_err(crate::Error::from)?; - - if !exists { - return Ok(exists); - } - - client - .execute_procedure(procedures::RevokeData { - location: location.into(), - should_gc: true, - }) - .map_err(|err| procedure_error::(vec![location.clone()], err)) - .map_err(crate::Error::from)?; - - Ok(exists) - }) - } - - async fn key_sign(&self, did: &CoreDID, location: &KeyLocation, data: Vec) -> Result { - let client: Client = self.client(&ClientPath::from(did))?; - - match location.key_type { - KeyType::Ed25519 => sign_ed25519(&client, data, location), - KeyType::X25519 => Err(identity_did::Error::InvalidMethodType.into()), - } - } - - async fn key_exists(&self, did: &CoreDID, location: &KeyLocation) -> Result { - let client: Client = self.client(&ClientPath::from(did))?; - - client - .record_exists(&location.into()) - .map_err(|err| StrongholdError::Vault(VaultOperation::RecordExists, err)) - .map_err(Into::into) - } - - #[cfg(feature = "encryption")] - async fn data_encrypt( - &self, - did: &CoreDID, - plaintext: Vec, - associated_data: Vec, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - public_key: PublicKey, - ) -> Result { - // Changes won't be written to the snapshot state since the created keys are temporary - let client: Client = self.client(&ClientPath::from(did))?; - let public_key: [u8; X25519::PUBLIC_KEY_LENGTH] = public_key - .as_ref() - .try_into() - .map_err(|_| Error::InvalidPublicKey(format!("expected public key of length {}", X25519::PUBLIC_KEY_LENGTH)))?; - match cek_algorithm { - CekAlgorithm::ECDH_ES(agreement) => { - let (derived_secret, ephemeral_public_key): (Location, PublicKey) = - diffie_hellman_with_concat_kdf(&client, encryption_algorithm, cek_algorithm, agreement, public_key).await?; - let encrypted_data: EncryptedData = aead_encrypt( - &client, - encryption_algorithm, - derived_secret, - plaintext, - associated_data, - Vec::new(), - ephemeral_public_key.as_ref().to_vec(), - ) - .await?; - Ok(encrypted_data) - } - CekAlgorithm::ECDH_ES_A256KW(agreement) => { - let (derived_secret, ephemeral_public_key): (Location, PublicKey) = - diffie_hellman_with_concat_kdf(&client, encryption_algorithm, cek_algorithm, agreement, public_key).await?; - let cek: Location = generate_content_encryption_key(&client, encryption_algorithm)?; - - let encrypted_cek: Vec = aes_256_wrap_key(&client, derived_secret, cek.clone())?; - - let encrypted_data: EncryptedData = aead_encrypt( - &client, - encryption_algorithm, - cek, - plaintext, - associated_data, - encrypted_cek, - ephemeral_public_key.as_ref().to_vec(), - ) - .await?; - Ok(encrypted_data) - } - } - } - - #[cfg(feature = "encryption")] - async fn data_decrypt( - &self, - did: &CoreDID, - data: EncryptedData, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - private_key: &KeyLocation, - ) -> Result> { - // Changes won't be written to the snapshot state since the created keys are temporary - let client: Client = self.client(&ClientPath::from(did))?; - let public_key: [u8; X25519::PUBLIC_KEY_LENGTH] = data - .ephemeral_public_key - .clone() - .try_into() - .map_err(|_| Error::InvalidPublicKey(format!("expected public key of length {}", X25519::PUBLIC_KEY_LENGTH)))?; - match cek_algorithm { - CekAlgorithm::ECDH_ES(agreement) => { - let shared_secret: Location = diffie_hellman(&client, private_key, public_key).await?; - let derived_secret: Location = concat_kdf( - &client, - encryption_algorithm, - cek_algorithm.name().to_owned(), - agreement, - shared_secret, - ) - .await?; - aead_decrypt(&client, encryption_algorithm, derived_secret, data).await - } - CekAlgorithm::ECDH_ES_A256KW(agreement) => { - let shared_secret: Location = diffie_hellman(&client, private_key, public_key).await?; - let derived_secret: Location = concat_kdf( - &client, - encryption_algorithm, - cek_algorithm.name().to_owned(), - agreement, - shared_secret, - ) - .await?; - - let cek: Location = aes_256_unwrap_key(&client, data.encrypted_cek.as_slice(), derived_secret)?; - - aead_decrypt(&client, encryption_algorithm, cek, data).await - } - } - } - - async fn blob_set(&self, did: &CoreDID, blob: Vec) -> Result<()> { - self.mutate_client(did, |client| { - let store: Store = client.store(); - - store - .insert(BLOB_STORE_KEY.as_bytes().to_vec(), blob, None) - .map(|_| ()) - .map_err(|err| StrongholdError::Store(StoreOperation::Insert, err).into()) - }) - } - - async fn blob_get(&self, did: &CoreDID) -> Result>> { - let client: Client = self.client(&ClientPath::from(did))?; - let store: Store = client.store(); - let data: Option> = store - .get(BLOB_STORE_KEY.as_bytes()) - .map_err(|err| StrongholdError::Store(StoreOperation::Get, err))?; - Ok(data) - } - - async fn flush_changes(&self) -> Result<()> { - self.persist_snapshot().await?; - - Ok(()) - } -} - -impl Drop for Stronghold { - fn drop(&mut self) { - if self.dropsave { - let _ = executor::block_on(self.flush_changes()); - } - } -} - -pub(crate) fn generate_private_key(client: &Client, location: &KeyLocation) -> Result<()> { - let generate_key: procedures::GenerateKey = procedures::GenerateKey { - ty: location_key_type(location), - output: location.into(), - }; - - client - .execute_procedure(generate_key) - .map_err(|err| procedure_error::(vec![location.clone()], err))?; - - Ok(()) -} - -pub(crate) fn insert_private_key(client: &Client, mut private_key: PrivateKey, location: &KeyLocation) -> Result<()> { - let stronghold_location: Location = location.into(); - - let vault: ClientVault = client.vault(stronghold_location.vault_path()); - - let private_key_vec: Vec = private_key.as_ref().to_vec(); - private_key.zeroize(); - - vault - .write_secret(stronghold_location, private_key_vec) - .map_err(|err| StrongholdError::Vault(VaultOperation::WriteSecret, err)) - .map_err(Into::into) -} - -pub(crate) fn retrieve_public_key(client: &Client, location: &KeyLocation) -> Result { - match location.key_type { - KeyType::Ed25519 | KeyType::X25519 => { - let public_key: procedures::PublicKey = procedures::PublicKey { - ty: location_key_type(location), - private_key: location.into(), - }; - - let public = client - .execute_procedure(public_key) - .map_err(|err| procedure_error::(vec![location.clone()], err))?; - - Ok(public.to_vec().into()) - } - } -} - -fn sign_ed25519(client: &Client, payload: Vec, location: &KeyLocation) -> Result { - let procedure: procedures::Ed25519Sign = procedures::Ed25519Sign { - private_key: location.into(), - msg: payload, - }; - - let signature: [u8; 64] = client - .execute_procedure(procedure) - .map_err(|err| procedure_error::(vec![location.clone()], err))?; - - Ok(Signature::new(signature.into())) -} - -pub(crate) async fn diffie_hellman( - client: &Client, - private_key: &KeyLocation, - public_key: [u8; X25519::PUBLIC_KEY_LENGTH], -) -> Result { - let location: [u8; 32] = rand::Rng::gen(&mut rand::thread_rng()); - let shared_key: Location = Location::generic(VAULT_PATH.to_vec(), location.to_vec()); - let diffie_hellman: procedures::X25519DiffieHellman = procedures::X25519DiffieHellman { - public_key, - private_key: private_key.into(), - shared_key: shared_key.clone(), - }; - client - .execute_procedure(diffie_hellman) - .map_err(|err| procedure_error::(vec![private_key.clone()], err))?; - Ok(shared_key) -} - -pub(crate) async fn concat_kdf( - client: &Client, - encryption_algorithm: &EncryptionAlgorithm, - algorithm_id: String, - agreement: &AgreementInfo, - shared_secret: Location, -) -> Result { - let location: [u8; 32] = rand::Rng::gen(&mut rand::thread_rng()); - let output: Location = Location::generic(VAULT_PATH.to_vec(), location.to_vec()); - let derived_secret: procedures::ConcatKdf = { - match encryption_algorithm { - EncryptionAlgorithm::AES256GCM => procedures::ConcatKdf { - hash: Sha2Hash::Sha256, - algorithm_id, - shared_secret, - key_len: Aes256Gcm::KEY_LENGTH, - apu: agreement.apu.clone(), - apv: agreement.apv.clone(), - pub_info: agreement.pub_info.clone(), - priv_info: agreement.priv_info.clone(), - output: output.clone(), - }, - } - }; - client - .execute_procedure(derived_secret) - .map_err(|err| procedure_error::(vec![], err))?; - Ok(output) -} - -pub(crate) async fn aead_encrypt( - client: &Client, - algorithm: &EncryptionAlgorithm, - key: Location, - plaintext: Vec, - associated_data: Vec, - encrypted_cek: Vec, - ephemeral_public_key: Vec, -) -> Result { - match algorithm { - EncryptionAlgorithm::AES256GCM => { - let nonce: &[u8] = &Aes256Gcm::random_nonce().map_err(Error::EncryptionFailure)?; - let aead_encrypt: procedures::AeadEncrypt = procedures::AeadEncrypt { - cipher: procedures::AeadCipher::Aes256Gcm, - associated_data: associated_data.clone(), - plaintext, - nonce: nonce.to_vec(), - key, - }; - let mut data = client - .execute_procedure(aead_encrypt) - .map_err(|err| procedure_error::(vec![], err))?; - Ok(EncryptedData::new( - nonce.to_vec(), - associated_data, - data.drain(..Aes256Gcm::TAG_LENGTH).collect(), - data, - encrypted_cek, - ephemeral_public_key, - )) - } - } -} - -pub(crate) async fn aead_decrypt( - client: &Client, - algorithm: &EncryptionAlgorithm, - key: Location, - encrypted_data: EncryptedData, -) -> Result> { - match algorithm { - EncryptionAlgorithm::AES256GCM => { - let aead_decrypt: procedures::AeadDecrypt = procedures::AeadDecrypt { - cipher: procedures::AeadCipher::Aes256Gcm, - key, - ciphertext: encrypted_data.ciphertext, - associated_data: encrypted_data.associated_data, - tag: encrypted_data.tag, - nonce: encrypted_data.nonce, - }; - let data = client - .execute_procedure(aead_decrypt) - .map_err(|err| procedure_error::(vec![], err))?; - Ok(data) - } - } -} - -/// Creates an ephemeral pair of X25519 keys, obtains the shared secret by runnning the Diffie-Hellman algorithm and -/// derives key material for use in encryption/decryption through application of the Concatenation Key Derivation -/// function. -/// -/// Returns the location of the dervied key material and the ephemeral public key. -async fn diffie_hellman_with_concat_kdf( - client: &Client, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - agreement: &AgreementInfo, - public_key: [u8; X25519::PUBLIC_KEY_LENGTH], -) -> Result<(Location, PublicKey)> { - //Generate ephemeral key - let ephemeral_location = random_location(KeyType::X25519); - generate_private_key(client, &ephemeral_location)?; - let ephemeral_public_key: PublicKey = retrieve_public_key(client, &ephemeral_location)?; - // Obtain the shared secret by combining the ephemeral key and the static public key - let shared_key: Location = diffie_hellman(client, &ephemeral_location, public_key).await?; - let derived_secret: Location = concat_kdf( - client, - encryption_algorithm, - cek_algorithm.name().to_owned(), - agreement, - shared_key, - ) - .await?; - Ok((derived_secret, ephemeral_public_key)) -} - -/// Generate a random content encryption key with the required length for `encryption_algorithm`. -fn generate_content_encryption_key(client: &Client, encryption_algorithm: &EncryptionAlgorithm) -> Result { - let _len: usize = encryption_algorithm.key_length(); - - // TODO: X25519 happens to match Aes256Gcm::KEY_LENGTH, but a proper solution is required. - // See https://github.com/iotaledger/stronghold.rs/issues/374 - let location: KeyLocation = random_location(KeyType::X25519); - generate_private_key(client, &location)?; - - Ok((&location).into()) -} - -/// Apply AES256 key wrap to the `cek` using `encryption_key` for encryption. -fn aes_256_wrap_key(client: &Client, encryption_key: Location, cek: Location) -> Result> { - let encrypted_cek: Vec = client - .execute_procedure(procedures::AesKeyWrapEncrypt { - cipher: procedures::AesKeyWrapCipher::Aes256, - encryption_key, - wrap_key: cek, - }) - .map_err(|err| procedure_error::(vec![], err))?; - Ok(encrypted_cek) -} - -/// Unwrap the given `encrypted_key` using `decryption_key`. -fn aes_256_unwrap_key(client: &Client, encrypted_key: impl AsRef<[u8]>, decryption_key: Location) -> Result { - let output: Location = random_stronghold_location(); - - client - .execute_procedure(procedures::AesKeyWrapDecrypt { - cipher: procedures::AesKeyWrapCipher::Aes256, - decryption_key, - wrapped_key: encrypted_key.as_ref().to_vec(), - output: output.clone(), - }) - .map_err(|err| procedure_error::(vec![], err))?; - - Ok(output) -} - -// Moves a key from one location to another, deleting the old one. -fn move_key(client: &Client, source: &KeyLocation, target: &KeyLocation) -> Result<()> { - let source_location: Location = source.into(); - let target_location: Location = target.into(); - - let copy_record = procedures::CopyRecord { - source: source_location.clone(), - target: target_location, - }; - - client - .execute_procedure(copy_record) - .map_err(|err| procedure_error::(vec![source.clone(), target.clone()], err))?; - - let revoke_data = procedures::RevokeData { - location: source_location, - should_gc: true, - }; - - client - .execute_procedure(revoke_data) - .map_err(|err| procedure_error::(vec![source.clone()], err))?; - - Ok(()) -} - -fn get_index(store: &Store) -> Result> { - let data: Option> = store - .get(INDEX_STORE_KEY.as_bytes()) - .map_err(|err| StrongholdError::Store(StoreOperation::Get, err))?; - - let index: BTreeSet = match data { - Some(index_vec) => BTreeSet::::from_json_slice(&index_vec)?, - None => BTreeSet::new(), - }; - - Ok(index) -} - -fn set_index(store: &Store, index: BTreeSet) -> Result<()> { - let index_vec: Vec = index.to_json_vec()?; - - store - .insert(INDEX_STORE_KEY.as_bytes().to_vec(), index_vec, None) - .map_err(|err| StrongholdError::Store(StoreOperation::Insert, err))?; - - Ok(()) -} - -impl From<&KeyLocation> for Location { - fn from(key_location: &KeyLocation) -> Self { - let record_path: Vec = key_location.canonical().into_bytes(); - Location::generic(VAULT_PATH.to_vec(), record_path) - } -} - -fn location_key_type(location: &KeyLocation) -> procedures::KeyType { - match location.key_type { - KeyType::Ed25519 => procedures::KeyType::Ed25519, - KeyType::X25519 => procedures::KeyType::X25519, - } -} - -pub(crate) fn random_location(key_type: KeyType) -> KeyLocation { - let mut thread_rng: rand::rngs::ThreadRng = rand::thread_rng(); - let fragment: String = rand::distributions::Alphanumeric.sample_string(&mut thread_rng, 32); - let public_key: [u8; 32] = rand::Rng::gen(&mut thread_rng); - - KeyLocation::new(key_type, fragment, &public_key) -} - -fn random_stronghold_location() -> Location { - let mut thread_rng: rand::rngs::ThreadRng = rand::thread_rng(); - let record_path: [u8; 32] = rand::Rng::gen(&mut thread_rng); - Location::generic(VAULT_PATH.to_vec(), record_path.to_vec()) -} - -fn procedure_error

(locations: Vec, err: ProcedureError) -> StrongholdError { - StrongholdError::Procedure(std::any::type_name::

(), locations, err) -} diff --git a/identity_account_storage/src/storage/test_suite.rs b/identity_account_storage/src/storage/test_suite.rs deleted file mode 100644 index 08038e0fe4..0000000000 --- a/identity_account_storage/src/storage/test_suite.rs +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Context; -use function_name::named; -use identity_did::did::CoreDID; -use rand::distributions::DistString; -use rand::rngs::OsRng; - -use identity_core::convert::FromJson; -use identity_core::convert::ToJson; -use identity_core::crypto::KeyPair; -use identity_core::crypto::KeyType; -use identity_core::crypto::PrivateKey; -use identity_core::crypto::PublicKey; -use identity_iota_core_legacy::did::IotaDID; -use identity_iota_core_legacy::document::IotaDocument; -use identity_iota_core_legacy::document::IotaVerificationMethod; -use identity_iota_core_legacy::tangle::MessageId; -use identity_iota_core_legacy::tangle::Network; -use identity_iota_core_legacy::tangle::NetworkName; - -use crate::identity::ChainState; -use crate::types::AgreementInfo; -use crate::types::CekAlgorithm; -use crate::types::DIDType; -use crate::types::EncryptedData; -use crate::types::EncryptionAlgorithm; -use crate::types::KeyLocation; -use crate::types::Signature; - -use super::Storage; - -macro_rules! ensure { - ($cond:expr, $($msg:expr),*) => {{ - if !$cond { - let message: String = format!($( $msg, )*); - let fn_name: &'static str = function_name!(); - return Err(anyhow::Error::msg(format!("[{}]: {}", fn_name, message))); - } - };}; -} - -macro_rules! ensure_eq { - ($left:expr, $right:expr, $($msg:expr),*) => { - ensure!($left == $right, $($msg),*); - }; -} - -fn random_string() -> String { - rand::distributions::Alphanumeric.sample_string(&mut OsRng, 32) -} - -/// A test suite for the `Storage` interface. -/// -/// This contains a set of tests that a correct storage implementation -/// should pass. Note that not every edge case is tested. -/// -/// Tests usually rely on multiple interface methods being implemented, so they should only -/// be run on a fully implemented version. That's why there is not a single test case for every -/// interface method. -pub struct StorageTestSuite; - -impl StorageTestSuite { - #[named] - pub async fn did_create_private_key_test(storage: impl Storage) -> anyhow::Result<()> { - let fragment: String = random_string(); - let keypair: KeyPair = KeyPair::new(KeyType::Ed25519).unwrap(); - let network: NetworkName = Network::Mainnet.name(); - - let expected_did: CoreDID = IotaDID::new_with_network(keypair.public().as_ref(), network.clone()) - .unwrap() - .into(); - let expected_location: KeyLocation = - KeyLocation::new(KeyType::Ed25519, fragment.clone(), keypair.public().as_ref()); - - let (did, location): (CoreDID, KeyLocation) = storage - .did_create( - DIDType::IotaDID, - network.clone(), - &fragment, - Some(keypair.private().to_owned()), - ) - .await - .context("did_create returned an error")?; - - ensure_eq!( - did, - expected_did, - "expected returned did to be `{expected_did}`, was `{did}`" - ); - - ensure_eq!( - location, - expected_location, - "expected returned location to be `{expected_location}`, was `{location}`" - ); - - let exists: bool = storage - .key_exists(&did, &location) - .await - .context("key_exists returned an error")?; - - ensure!(exists, "expected key at location `{location}` to exist"); - - let result: Result<_, crate::Error> = storage - .did_create(DIDType::IotaDID, network, &fragment, Some(keypair.private().to_owned())) - .await; - - ensure!( - result.is_err(), - "expected did_create to return an error when attempting to create an identity from the same private key twice" - ); - - let public_key: PublicKey = storage - .key_public(&did, &location) - .await - .context("key_public returned an error")?; - - ensure_eq!( - public_key.as_ref(), - keypair.public().as_ref(), - "expected key_public to return `{:?}`, returned `{public_key:?}`", - keypair.public() - ); - - Ok(()) - } - - #[named] - pub async fn did_create_generate_key_test(storage: impl Storage) -> anyhow::Result<()> { - let fragment: String = random_string(); - let network: NetworkName = Network::Devnet.name(); - let (core_did, location): (CoreDID, KeyLocation) = storage - .did_create(DIDType::IotaDID, network.clone(), &fragment, None) - .await - .context("did_create returned an error")?; - let did: IotaDID = IotaDID::try_from(core_did.clone()).unwrap(); - ensure_eq!( - did.network_str(), - network.as_ref(), - "expected network `{network}` for the generated DID, was `{}`", - did.network_str() - ); - - let exists: bool = storage - .key_exists(&core_did, &location) - .await - .context("key_exists returned an error")?; - - ensure!(exists, "expected key at location `{location}` to exist"); - - let public_key: PublicKey = storage - .key_public(&core_did, &location) - .await - .context("key_public returned an error")?; - - let expected_did: IotaDID = IotaDID::new_with_network(public_key.as_ref(), network).unwrap(); - - ensure_eq!( - did, - expected_did, - "returned did `{did}` did not match did created from retrieved public key and network, expected: `{expected_did}`" - ); - - Ok(()) - } - - #[named] - pub async fn key_generate_test(storage: impl Storage) -> anyhow::Result<()> { - let fragment: String = random_string(); - let network: NetworkName = Network::Mainnet.name(); - - let (did, _): (CoreDID, _) = storage - .did_create(DIDType::IotaDID, network.clone(), &fragment, None) - .await - .context("did_create returned an error")?; - - let key_types: [KeyType; 2] = [KeyType::Ed25519, KeyType::X25519]; - - let mut locations: Vec = Vec::with_capacity(key_types.len()); - - for key_type in key_types { - let key_fragment: String = random_string(); - let location: KeyLocation = storage - .key_generate(&did, key_type, &key_fragment) - .await - .context("key_generate returned an error")?; - locations.push(location); - } - - for location in locations { - let exists: bool = storage - .key_exists(&did, &location) - .await - .context("key_exists returned an error")?; - - ensure!(exists, "expected key at location `{location}` to exist"); - - // Ensure we can retrieve the public key without erroring. - storage - .key_public(&did, &location) - .await - .context("key_public returned an error")?; - } - - Ok(()) - } - - #[named] - pub async fn key_delete_test(storage: impl Storage) -> anyhow::Result<()> { - const NUM_IDENTITIES: usize = 20; - let fragment: String = random_string(); - let network: NetworkName = Network::Mainnet.name(); - - let (did, _): (CoreDID, _) = storage - .did_create(DIDType::IotaDID, network.clone(), &fragment, None) - .await - .context("did_create returned an error")?; - - let mut locations: Vec = Vec::with_capacity(NUM_IDENTITIES); - - for _ in 0..NUM_IDENTITIES { - let key_fragment: String = random_string(); - let location: KeyLocation = storage - .key_generate(&did, KeyType::Ed25519, &key_fragment) - .await - .context("key_generate returned an error")?; - locations.push(location); - } - - for location in locations { - let exists: bool = storage - .key_exists(&did, &location) - .await - .context("key_exists returned an error")?; - - ensure!(exists, "expected key at location `{location}` to exist"); - - let deleted: bool = storage - .key_delete(&did, &location) - .await - .context("key_delete returned an error")?; - - ensure!(deleted, "expected key at location `{location}` to be deleted"); - - let deleted: bool = storage - .key_delete(&did, &location) - .await - .context("key_delete returned an error")?; - - ensure!(!deleted, "expected key at location `{location}` to already be deleted"); - } - - Ok(()) - } - - #[named] - pub async fn did_list_test(storage: impl Storage) -> anyhow::Result<()> { - const NUM_IDENTITIES: usize = 20; - let fragment: String = random_string(); - let network: NetworkName = Network::Mainnet.name(); - - let list: Vec = storage.did_list().await.context("did_list returned an error")?; - - ensure!( - list.is_empty(), - "expected list to be empty, but found {} element(s)", - list.len() - ); - - for i in 0..NUM_IDENTITIES { - let (did, _): (CoreDID, _) = storage - .did_create(DIDType::IotaDID, network.clone(), &fragment, None) - .await - .context("did_create returned an error")?; - - let exists: bool = storage.did_exists(&did).await.context("did_exists returned an error")?; - ensure!(exists, "expected did `{did}` to exist"); - - let list_len: usize = storage.did_list().await.context("did_list returned an error")?.len(); - let expected_len: usize = i + 1; - - ensure_eq!( - list_len, - expected_len, - "expected did_list to return a list of len {expected_len}, got {list_len} elements instead" - ); - } - - Ok(()) - } - - #[named] - pub async fn key_insert_test(storage: impl Storage) -> anyhow::Result<()> { - let fragment: String = random_string(); - let network: NetworkName = Network::Mainnet.name(); - - let (did, _): (CoreDID, _) = storage - .did_create(DIDType::IotaDID, network.clone(), &fragment, None) - .await - .context("did_create returned an error")?; - - let key_types: [KeyType; 2] = [KeyType::Ed25519, KeyType::X25519]; - - let mut locations: Vec = Vec::with_capacity(key_types.len()); - let mut public_keys: Vec = Vec::with_capacity(key_types.len()); - - for key_type in key_types { - let key_fragment: String = random_string(); - let keypair: KeyPair = KeyPair::new(key_type).unwrap(); - let location: KeyLocation = KeyLocation::new(key_type, key_fragment, keypair.public().as_ref()); - - storage - .key_insert(&did, &location, keypair.private().to_owned()) - .await - .context("key_insert returned an error")?; - - public_keys.push(keypair.public().to_owned()); - locations.push(location); - } - - for (i, location) in locations.into_iter().enumerate() { - let exists: bool = storage - .key_exists(&did, &location) - .await - .context("key_exists returned an error")?; - - ensure!(exists, "expected key at location `{location}` to exist"); - - let public_key: PublicKey = storage - .key_public(&did, &location) - .await - .context("key_public returned an error")?; - - let expected_public_key: &PublicKey = &public_keys[i]; - - ensure_eq!( - public_key.as_ref(), - expected_public_key.as_ref(), - "expected public key at location `{location}` to be {expected_public_key:?}, was {public_key:?}" - ); - } - - Ok(()) - } - - #[named] - pub async fn key_sign_ed25519_test(storage: impl Storage) -> anyhow::Result<()> { - // The following test vector is taken from Test 2 of RFC 8032 - // https://datatracker.ietf.org/doc/html/rfc8032#section-7 - const PRIVATE_KEY: [u8; 32] = [ - 76, 205, 8, 155, 40, 255, 150, 218, 157, 182, 195, 70, 236, 17, 78, 15, 91, 138, 49, 159, 53, 171, 166, 36, 218, - 140, 246, 237, 79, 184, 166, 251, - ]; - const MESSAGE: [u8; 1] = [114]; - const SIGNATURE: [u8; 64] = [ - 146, 160, 9, 169, 240, 212, 202, 184, 114, 14, 130, 11, 95, 100, 37, 64, 162, 178, 123, 84, 22, 80, 63, 143, 179, - 118, 34, 35, 235, 219, 105, 218, 8, 90, 193, 228, 62, 21, 153, 110, 69, 143, 54, 19, 208, 241, 29, 140, 56, 123, - 46, 174, 180, 48, 42, 238, 176, 13, 41, 22, 18, 187, 12, 0, - ]; - - let fragment: String = random_string(); - let network: NetworkName = Network::Mainnet.name(); - - let (did, location): (CoreDID, KeyLocation) = storage - .did_create( - DIDType::IotaDID, - network.clone(), - &fragment, - Some(PrivateKey::from(PRIVATE_KEY.to_vec())), - ) - .await - .context("did_create returned an error")?; - - let signature: Signature = storage - .key_sign(&did, &location, MESSAGE.to_vec()) - .await - .context("key_sign returned an error")?; - - ensure_eq!( - signature.as_bytes(), - &SIGNATURE, - "expected signature to be `{SIGNATURE:?}`, was `{:?}`", - signature.as_bytes() - ); - - Ok(()) - } - - #[named] - pub async fn key_value_store_test(storage: impl Storage) -> anyhow::Result<()> { - let fragment: String = random_string(); - let network: NetworkName = Network::Mainnet.name(); - - let (did, location): (CoreDID, KeyLocation) = storage - .did_create(DIDType::IotaDID, network.clone(), &fragment, None) - .await - .context("did_create returned an error")?; - - let value: Option> = storage.blob_get(&did).await.context("blob_get returned an error")?; - - ensure!(value.is_none(), "expected blob_get to return `None` for a new DID"); - - let public_key: PublicKey = storage - .key_public(&did, &location) - .await - .context("key_public returned an error")?; - - let method: IotaVerificationMethod = IotaVerificationMethod::new( - did.clone().try_into().unwrap(), - KeyType::Ed25519, - &public_key, - &fragment, - ) - .unwrap(); - - let expected_document: IotaDocument = IotaDocument::from_verification_method(method).unwrap(); - storage - .blob_set(&did, expected_document.to_json_vec().unwrap()) - .await - .context("blob_set returned an error")?; - let value: Option> = storage.blob_get(&did).await.context("blob_get returned an error")?; - let document: IotaDocument = IotaDocument::from_json_slice(&value.unwrap()).unwrap(); - ensure_eq!( - expected_document, - document, - "expected `{expected_document}`, got `{document}`" - ); - - let mut expected_chain_state: ChainState = ChainState::new(); - expected_chain_state.set_last_integration_message_id(MessageId::new([0xff; 32])); - storage - .blob_set(&did, expected_chain_state.to_json_vec().unwrap()) - .await - .context("blob_set returned an error")?; - let value: Option> = storage.blob_get(&did).await.context("blob_get returned an error")?; - let chain_state: ChainState = ChainState::from_json_slice(&value.unwrap()).unwrap(); - ensure_eq!( - expected_chain_state, - chain_state, - "expected `{expected_chain_state:?}`, got `{chain_state:?}`" - ); - - Ok(()) - } - - #[named] - pub async fn did_purge_test(storage: impl Storage) -> anyhow::Result<()> { - let fragment: String = random_string(); - let network: NetworkName = Network::Mainnet.name(); - - let (did, location): (CoreDID, KeyLocation) = storage - .did_create(DIDType::IotaDID, network.clone(), &fragment, None) - .await - .context("did_create returned an error")?; - - let list_len: usize = storage.did_list().await.context("did_list returned an error")?.len(); - - ensure_eq!( - list_len, - 1, - "expected did_list to return a list of size 1 after creation" - ); - - let mut expected_chain_state: ChainState = ChainState::new(); - expected_chain_state.set_last_integration_message_id(MessageId::new([0xff; 32])); - - storage - .blob_set(&did, expected_chain_state.to_json_vec().unwrap()) - .await - .context("chain_state_set returned an error")?; - - let purged: bool = storage.did_purge(&did).await.context("did_purge returned an error")?; - - ensure!(purged, "expected did `{did}` to have been purged"); - - let value: Option> = storage.blob_get(&did).await.context("blob_get returned an error")?; - - ensure!(value.is_none(), "expected blob_get to return `None` after purging"); - - let exists: bool = storage - .key_exists(&did, &location) - .await - .context("key_exists returned an error")?; - - ensure!( - !exists, - "expected key at location `{location}` to no longer exist after purge" - ); - - let list: Vec = storage.did_list().await.context("did_list returned an error")?; - - ensure!( - list.is_empty(), - "expected did_list to return an empty list after purging" - ); - - Ok(()) - } - - #[named] - pub async fn encryption_test(alice_storage: impl Storage, bob_storage: impl Storage) -> anyhow::Result<()> { - let agreement: AgreementInfo = AgreementInfo::new(b"Alice".to_vec(), b"Bob".to_vec(), Vec::new(), Vec::new()); - - for cek_algorithm in [ - CekAlgorithm::ECDH_ES(agreement.clone()), - CekAlgorithm::ECDH_ES_A256KW(agreement), - ] { - let network: NetworkName = Network::Mainnet.name(); - - // Both Alice (Sender) and Bob (Receiver) must have a DID. - let (alice_did, _): (CoreDID, KeyLocation) = alice_storage - .did_create(DIDType::IotaDID, network.clone(), &random_string(), None) - .await - .context("did_create returned an error")?; - - let (bob_did, _): (CoreDID, KeyLocation) = bob_storage - .did_create(DIDType::IotaDID, network.clone(), &random_string(), None) - .await - .context("did_create returned an error")?; - - // The target of the message must share an X25519 public key. - let bob_fragment: String = random_string(); - let bob_location: KeyLocation = bob_storage - .key_generate(&bob_did, KeyType::X25519, &bob_fragment) - .await - .context("key_generate returned an error")?; - let bob_public_key: PublicKey = bob_storage - .key_public(&bob_did, &bob_location) - .await - .context("key_public returned an error")?; - - // Alice encrypts the message to be sent to Bob. - let encryption_algorithm: EncryptionAlgorithm = EncryptionAlgorithm::AES256GCM; - let plaintext: &[u8] = b"This msg will be encrypted and decrypted"; - - let encrypted_data: EncryptedData = alice_storage - .data_encrypt( - &alice_did, - plaintext.to_vec(), - b"associated_data".to_vec(), - &encryption_algorithm, - &cek_algorithm, - bob_public_key, - ) - .await - .context("data_encrypt returned an error")?; - - // Bob must be able to decrypt the message using the shared secret. - let decrypted_msg: Vec = bob_storage - .data_decrypt( - &bob_did, - encrypted_data, - &encryption_algorithm, - &cek_algorithm, - &bob_location, - ) - .await - .context("data_decrypt returned an error")?; - - ensure_eq!( - plaintext, - &decrypted_msg, - "decrypted message does not match the original message" - ); - } - - Ok(()) - } -} diff --git a/identity_account_storage/src/storage/traits.rs b/identity_account_storage/src/storage/traits.rs deleted file mode 100644 index e09600aced..0000000000 --- a/identity_account_storage/src/storage/traits.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Debug; - -use async_trait::async_trait; - -use identity_core::crypto::KeyType; -use identity_core::crypto::PrivateKey; -use identity_core::crypto::PublicKey; -use identity_did::did::CoreDID; -use identity_iota_core_legacy::tangle::NetworkName; - -use crate::error::Result; -#[cfg(feature = "encryption")] -use crate::types::CekAlgorithm; -use crate::types::DIDType; -#[cfg(feature = "encryption")] -use crate::types::EncryptedData; -#[cfg(feature = "encryption")] -use crate::types::EncryptionAlgorithm; -use crate::types::KeyLocation; -use crate::types::Signature; - -#[cfg(not(feature = "send-sync-storage"))] -mod storage_sub_trait { - pub trait StorageSendSyncMaybe {} - impl StorageSendSyncMaybe for S {} -} - -#[cfg(feature = "send-sync-storage")] -mod storage_sub_trait { - pub trait StorageSendSyncMaybe: Send + Sync {} - impl StorageSendSyncMaybe for S {} -} - -/// An interface for Account storage implementations. -/// -/// The [`Storage`] interface is used for secure key operations, such as key generation and signing, -/// as well as key-value like storage of data structures, such as DID documents. -/// -/// # Identifiers -/// -/// Implementations of this interface are expected to uniquely identify keys through the -/// combination of DID _and_ `KeyLocation`. -/// -/// An implementation recommendation is to use the DID as a partition key. Everything related to a DID -/// can be stored in a partition identified by that DID. Keys belonging to a DID can then be identified -/// by [`KeyLocation`]s in that partition. -/// -/// # DID List -/// -/// The storage is expected to maintain a list of stored DIDs. DIDs created with `did_create` should be -/// inserted into the list, and removed when calling `did_purge`. -/// Other operations on the list are `did_exists` and `did_list`. -/// -/// # Thread-Safety -/// -/// Note: This only applies if the `send-sync-storage` feature is enabled. -/// -/// Since the DID list is a global data structure per storage instance, modifications to that list -/// need to be carefully synchronized. -/// Other operations can be executed concurrently and don't need to be synchronized globally, -/// as it is a user error to create more than one `Account` for the same identity. -/// Regardless of that, a storage implementation still needs to be thread-safe as defined by -/// the `Send` and `Sync` traits. -/// -/// # Implementation example -/// -/// See [`MemStore`][crate::storage::MemStore] for a test/example implementation. -#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))] -#[cfg_attr(feature = "send-sync-storage", async_trait)] -pub trait Storage: storage_sub_trait::StorageSendSyncMaybe + Debug { - /// Creates a new identity of the type declared in `did_type` for the given `network`. - /// - /// - Uses the given Ed25519 `private_key` or generates a new key if it's `None`. - /// - Returns an error if the DID already exists. - /// - Adds the newly created DID to a list which can be accessed via [`Storage::did_list`]. - /// - /// Returns the generated DID represented as a [`CoreDID`] and the location at which the key was stored. - async fn did_create( - &self, - did_type: DIDType, - network: NetworkName, - fragment: &str, - private_key: Option, - ) -> Result<(CoreDID, KeyLocation)>; - - /// Removes the keys and any other state for the given `did`. - /// - /// This operation is idempotent: it does not fail if the given `did` does not (or no longer) exist. - /// - /// Returns `true` if the did and its associated data was removed, `false` if nothing was done. - async fn did_purge(&self, did: &CoreDID) -> Result; - - /// Returns `true` if `did` exists in the list of stored DIDs. - async fn did_exists(&self, did: &CoreDID) -> Result; - - /// Returns the list of stored DIDs. - async fn did_list(&self) -> Result>; - - /// Generates a new key for the given `did` with the given `key_type` and `fragment` identifier - /// and returns the location of the newly generated key. - async fn key_generate(&self, did: &CoreDID, key_type: KeyType, fragment: &str) -> Result; - - /// Inserts a private key at the specified `location`. - /// - /// If a key at `location` exists, it is overwritten. - async fn key_insert(&self, did: &CoreDID, location: &KeyLocation, private_key: PrivateKey) -> Result<()>; - - /// Retrieves the public key from `location`. - async fn key_public(&self, did: &CoreDID, location: &KeyLocation) -> Result; - - /// Deletes the key at `location`. - /// - /// This operation is idempotent: it does not fail if the key does not exist. - /// - /// Returns `true` if it removed the key, `false` if nothing was done. - async fn key_delete(&self, did: &CoreDID, location: &KeyLocation) -> Result; - - /// Signs `data` with the private key at the specified `location`. - async fn key_sign(&self, did: &CoreDID, location: &KeyLocation, data: Vec) -> Result; - - /// Returns `true` if a key exists at the specified `location`. - async fn key_exists(&self, did: &CoreDID, location: &KeyLocation) -> Result; - - /// Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`. - /// - /// Returns an [`EncryptedData`] instance. - #[cfg(feature = "encryption")] - async fn data_encrypt( - &self, - did: &CoreDID, - plaintext: Vec, - associated_data: Vec, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - public_key: PublicKey, - ) -> Result; - - /// Decrypts the given `data` with the specified `encryption_algorithm` and `cek_algorithm`. - /// - /// Returns the decrypted text. - #[cfg(feature = "encryption")] - async fn data_decrypt( - &self, - did: &CoreDID, - data: EncryptedData, - encryption_algorithm: &EncryptionAlgorithm, - cek_algorithm: &CekAlgorithm, - private_key: &KeyLocation, - ) -> Result>; - - /// Stores an arbitrary blob for the identity specified by `did`. - async fn blob_set(&self, did: &CoreDID, blob: Vec) -> Result<()>; - - /// Returns the blob stored by the identity specified by `did`. - async fn blob_get(&self, did: &CoreDID) -> Result>>; - - /// Persists any unsaved changes. - async fn flush_changes(&self) -> Result<()>; -} diff --git a/identity_account_storage/src/types/did_type.rs b/identity_account_storage/src/types/did_type.rs deleted file mode 100644 index cabfd8b3a5..0000000000 --- a/identity_account_storage/src/types/did_type.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -/// Supported types representing a DID that can be generated by the [`Storage`](crate::storage::Storage) interface. -#[derive(Clone, Copy, Debug)] -pub enum DIDType { - /// Corresponds to [`IotaDID`](identity_iota_core_legacy::did::IotaDID). - IotaDID, -} diff --git a/identity_account_storage/src/types/key_location.rs b/identity_account_storage/src/types/key_location.rs deleted file mode 100644 index d646c1cc30..0000000000 --- a/identity_account_storage/src/types/key_location.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Debug; -use core::fmt::Display; -use core::fmt::Formatter; -use core::fmt::Result; -use identity_core::crypto::KeyType; -use identity_did::verification::MethodData; -use identity_did::verification::MethodType; -use identity_iota_core_legacy::document::IotaVerificationMethod; -use seahash::SeaHasher; -use std::hash::Hash; -use std::hash::Hasher; - -/// The storage location of a verification method key. -/// -/// A key is uniquely identified by the fragment and a hash of its public key. -/// Importantly, the fragment alone is insufficient to represent the storage location. -/// For example, when rotating a key, there will be two keys in storage for the -/// same identity with the same fragment. The `key_hash` disambiguates the keys in -/// situations like these. -/// -/// The string representation of that location can be obtained via `canonical_repr`. -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct KeyLocation { - /// The [`KeyType`] of the key. - pub key_type: KeyType, - /// The fragment of the key. - fragment: String, - /// The hash of the public key. - pub(in crate::types::key_location) key_hash: String, -} - -impl KeyLocation { - /// Create a location from a [`KeyType`], the fragment of a verification method - /// and the bytes of a public key. - pub fn new(key_type: KeyType, fragment: String, public_key: &[u8]) -> Self { - let mut hasher = SeaHasher::new(); - hasher.write(public_key); - let key_hash: u64 = hasher.finish(); - - Self { - key_type, - fragment, - key_hash: key_hash.to_string(), - } - } - - /// Obtain the location of a verification method's key in storage. - pub fn from_verification_method(method: &IotaVerificationMethod) -> crate::Result { - let fragment: &str = method - .id() - .fragment() - .ok_or(crate::Error::DIDError(identity_did::Error::MissingIdFragment))?; - let method_data: &MethodData = method.data(); - - let key_type: KeyType = match method.type_() { - MethodType::Ed25519VerificationKey2018 => KeyType::Ed25519, - MethodType::X25519KeyAgreementKey2019 => KeyType::X25519, - }; - - let public_key: Vec = method_data.try_decode()?; - - Ok(KeyLocation::new(key_type, fragment.to_owned(), public_key.as_ref())) - } - - /// Returns the canonical string representation of the location. - /// - /// This should be used as the representation for storage keys. - pub fn canonical(&self) -> String { - format!("{}:{}", self.fragment, self.key_hash) - } -} - -impl Display for KeyLocation { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_str(&self.canonical()) - } -} - -// Custom Hash and Equality implementations to not include the key_type. - -impl Hash for KeyLocation { - fn hash(&self, state: &mut H) { - state.write(self.canonical().as_bytes()); - } -} - -impl PartialEq for KeyLocation { - fn eq(&self, other: &Self) -> bool { - self.fragment == other.fragment && self.key_hash == other.key_hash - } -} - -impl Eq for KeyLocation {} - -#[cfg(test)] -mod tests { - use identity_core::crypto::KeyType; - use rand::distributions::DistString; - use rand::rngs::OsRng; - - use super::KeyLocation; - - // These same test vector should also be tested in Wasm - // to ensure hashes are consistent across architectures. - - static TEST_VECTOR_1: ([u8; 32], &str) = ( - [ - 187, 104, 26, 87, 133, 152, 0, 180, 17, 232, 218, 46, 190, 140, 102, 34, 42, 94, 9, 101, 87, 249, 167, 237, 194, - 182, 240, 2, 150, 78, 110, 218, - ], - "74874706796298672", - ); - - static TEST_VECTOR_2: ([u8; 32], &str) = ( - [ - 125, 153, 99, 21, 23, 190, 149, 109, 84, 120, 40, 91, 181, 57, 67, 254, 11, 25, 152, 214, 84, 46, 105, 186, 16, - 39, 141, 151, 100, 163, 138, 222, - ], - "10201576743536852223", - ); - - #[test] - fn test_key_location_canonical_representation() { - for (test_vector, expected_hash) in [TEST_VECTOR_1, TEST_VECTOR_2] { - let fragment: String = rand::distributions::Alphanumeric.sample_string(&mut OsRng, 32); - - let location: KeyLocation = KeyLocation::new(KeyType::Ed25519, fragment.clone(), &test_vector); - - let canonical_repr: String = location.canonical(); - - let mut parts = canonical_repr.split(':'); - - let fragment_str: &str = parts.next().unwrap(); - let key_hash_str: &str = parts.next().unwrap(); - - assert_eq!(fragment_str, &fragment); - assert_eq!(key_hash_str, expected_hash); - } - } -} diff --git a/identity_account_storage/src/types/mod.rs b/identity_account_storage/src/types/mod.rs deleted file mode 100644 index f2b3204116..0000000000 --- a/identity_account_storage/src/types/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod did_type; -#[cfg(feature = "encryption")] -mod encryption; -mod key_location; -mod signature; - -pub use self::did_type::*; -#[cfg(feature = "encryption")] -pub use self::encryption::*; -pub use self::key_location::*; -pub use self::signature::*; diff --git a/identity_account_storage/src/types/signature.rs b/identity_account_storage/src/types/signature.rs deleted file mode 100644 index bd8b147c60..0000000000 --- a/identity_account_storage/src/types/signature.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::Deserialize; -use serde::Serialize; - -/// A digital signature. -#[derive(Clone, Deserialize, Serialize)] -pub struct Signature(pub(crate) Vec); - -impl Signature { - /// Creates a `Signature`. - pub fn new(data: Vec) -> Self { - Signature(data) - } - - /// Returns the signature as a slice of bytes. - pub fn as_bytes(&self) -> &[u8] { - &self.0 - } -} - -impl From for Vec { - fn from(signature: Signature) -> Self { - signature.0 - } -} diff --git a/identity_account_storage/src/utils/fs.rs b/identity_account_storage/src/utils/fs.rs deleted file mode 100644 index a2ec7974a8..0000000000 --- a/identity_account_storage/src/utils/fs.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fs; -use std::path::Path; - -pub fn ensure_directory

(path: &P) -> Result<(), std::io::Error> -where - P: AsRef + ?Sized, -{ - if let Some(parent) = path.as_ref().parent() { - fs::create_dir_all(parent)?; - } - - Ok(()) -} diff --git a/identity_account_storage/src/utils/mod.rs b/identity_account_storage/src/utils/mod.rs deleted file mode 100644 index 8904969bd4..0000000000 --- a/identity_account_storage/src/utils/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod crypto; -mod shared; - -pub mod fs; - -pub use self::crypto::*; -pub use self::shared::*; diff --git a/identity_account_storage/src/utils/shared.rs b/identity_account_storage/src/utils/shared.rs deleted file mode 100644 index 131e9414dc..0000000000 --- a/identity_account_storage/src/utils/shared.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use core::fmt::Debug; -use core::fmt::Formatter; -use std::sync::RwLock; -use std::sync::RwLockReadGuard; -use std::sync::RwLockWriteGuard; - -use serde::Deserialize; -use serde::Serialize; - -use crate::error::Error; -use crate::error::Result; - -#[derive(Default, Deserialize, Serialize)] -pub struct Shared(RwLock); - -impl Shared { - pub fn new(data: T) -> Self { - Self(RwLock::new(data)) - } - - pub fn read(&self) -> Result> { - self.0.read().map_err(|_| Error::SharedReadPoisoned) - } - - pub fn write(&self) -> Result> { - self.0.write().map_err(|_| Error::SharedWritePoisoned) - } -} - -impl Debug for Shared { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - Debug::fmt(&self.0, f) - } -} diff --git a/identity_agent/Cargo.toml b/identity_agent/Cargo.toml index d37e5b07f3..bc205f24fa 100644 --- a/identity_agent/Cargo.toml +++ b/identity_agent/Cargo.toml @@ -20,14 +20,15 @@ log = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0", default-features = false } thiserror = { version = "1.0", default-features = false } -tokio = { version = "1.15", default-features = false, features = ["rt", "time"] } +tokio = { version = "1.21", default-features = false, features = ["rt", "time"] } uuid = { version = "0.8", default-features = false, features = ["v4", "serde"] } [dev-dependencies] criterion = { version = "0.3", default-features = false, features = ["stable"] } -identity_account = { version = "=0.6.1", default-features = false, features = ["send-sync-storage", "encryption", "async", "revocation-bitmap"] } -identity_iota_core = { version = "=0.6.1", default-features = false } +identity_iota_core = { path = "../identity_iota_core", version = "=0.7.0-alpha.3", default-features = false } pretty_env_logger = { version = "0.4", default-features = false } +rand = "0.8.5" +tokio = { version = "*", default-features = false, features = ["sync", "macros"] } [[bench]] name = "agent" diff --git a/identity_agent/benches/agent.rs b/identity_agent/benches/agent.rs index d2626b2d1f..dddea0a867 100644 --- a/identity_agent/benches/agent.rs +++ b/identity_agent/benches/agent.rs @@ -9,6 +9,9 @@ use identity_agent::agent::AgentBuilder; use identity_agent::agent::AgentId; use identity_agent::Multiaddr; +use identity_iota_core::IotaDID; +use identity_iota_core::IotaDocument; +use identity_iota_core::NetworkName; use remote_account::IdentityCreate; use remote_account::RemoteAccount; @@ -16,7 +19,7 @@ async fn setup() -> (Agent, AgentId, Agent) { let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0".parse().unwrap(); let mut builder = AgentBuilder::new(); - let remote_account = RemoteAccount::new().unwrap(); + let remote_account = RemoteAccount::new(); builder.attach::(remote_account); let mut receiver: Agent = builder.build().await.unwrap(); @@ -31,6 +34,16 @@ async fn setup() -> (Agent, AgentId, Agent) { (receiver, receiver_agent_id, sender) } +fn fake_document() -> IotaDocument { + let rand_bytes: [u8; 32] = rand::random(); + let network_name = NetworkName::try_from("iota").unwrap(); + let mut did = IotaDID::new(&rand_bytes, &network_name); + let mut doc = IotaDocument::new(&network_name); + // Let's act as if this was a published IotaDocument for testing purposes. + std::mem::swap(doc.core_document_mut().id_mut_unchecked(), &mut did); + doc +} + fn bench_remote_account(c: &mut Criterion) { let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -45,9 +58,11 @@ fn bench_remote_account(c: &mut Criterion) { bencher.to_async(&runtime).iter(|| { let mut sender_clone: Agent = sender.clone(); + let doc = fake_document(); + async move { sender_clone - .send_request(receiver_agent_id, IdentityCreate::default()) + .send_request(receiver_agent_id, IdentityCreate(doc)) .await .unwrap() .unwrap(); @@ -69,49 +84,37 @@ criterion_main!(benches); mod remote_account { use dashmap::DashMap; - use identity_account::account::Account; - use identity_account::account::AccountBuilder; - use identity_account::types::IdentitySetup; use identity_agent::agent::Endpoint; use identity_agent::agent::Handler; use identity_agent::agent::HandlerRequest; use identity_agent::agent::RequestContext; - use identity_iota_core::did::IotaDID; - use identity_iota_core::document::IotaDocument; + use identity_iota_core::IotaDID; + use identity_iota_core::IotaDocument; use serde::Deserialize; use serde::Serialize; use std::sync::Arc; - use tokio::sync::Mutex; + /// A proof-of-concept implementation of a remote account + /// which holds and manages a collection of DID documents. #[derive(Debug, Clone)] - pub struct RemoteAccount { - builder: Arc>, - accounts: Arc>, + pub(crate) struct RemoteAccount { + documents: Arc>, } impl RemoteAccount { - pub fn new() -> identity_account::Result { - let builder: AccountBuilder = Account::builder().autopublish(false); - - Ok(Self { - builder: Arc::new(Mutex::new(builder)), - accounts: Arc::new(DashMap::new()), - }) + pub(crate) fn new() -> Self { + Self { + documents: Arc::new(DashMap::new()), + } } } - /// Can be sent to a `RemoteAccount` to instruct it to create an identity. - #[derive(Debug, Default, Clone, Serialize, Deserialize)] - pub struct IdentityCreate; - - impl From for IdentitySetup { - fn from(_: IdentityCreate) -> Self { - IdentitySetup::default() - } - } + /// Can be sent to a `RemoteAccount` to instruct it to add a document. + #[derive(Debug, Clone, Serialize, Deserialize)] + pub(crate) struct IdentityCreate(pub(crate) IotaDocument); impl HandlerRequest for IdentityCreate { - type Response = Result; + type Response = Result<(), RemoteAccountError>; fn endpoint() -> Endpoint { "remote_account/create".try_into().unwrap() @@ -120,27 +123,25 @@ mod remote_account { #[async_trait::async_trait] impl Handler for RemoteAccount { - async fn handle(&self, request: RequestContext) -> Result { - let account: Account = self.builder.lock().await.create_identity(request.input.into()).await?; - let doc = account.document().to_owned(); - self.accounts.insert(account.did().to_owned(), account); - Ok(doc) + async fn handle(&self, request: RequestContext) -> Result<(), RemoteAccountError> { + let document = request.input.0; + + if document.id().is_placeholder() { + return Err(RemoteAccountError::PlaceholderDID); + } + + self.documents.insert(document.id().to_owned(), document); + Ok(()) } } /// The error type for the [`RemoteAccount`]. #[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)] #[non_exhaustive] - pub enum RemoteAccountError { + pub(crate) enum RemoteAccountError { #[error("identity not found")] IdentityNotFound, - #[error("{0}")] - AccountError(String), - } - - impl From for RemoteAccountError { - fn from(err: identity_account::Error) -> Self { - Self::AccountError(err.to_string()) - } + #[error("placeholder DIDs cannot be managed")] + PlaceholderDID, } } diff --git a/identity_agent/src/tests/handler.rs b/identity_agent/src/tests/handler.rs index feb76e3a00..3d7aa3c2bf 100644 --- a/identity_agent/src/tests/handler.rs +++ b/identity_agent/src/tests/handler.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use std::task::Poll; use futures::pin_mut; -use identity_iota_core::did::IotaDID; +use identity_iota_core::IotaDID; use libp2p::request_response::OutboundFailure; use libp2p::Multiaddr; @@ -123,7 +123,7 @@ async fn test_unknown_request_returns_error() -> AgentResult<()> { .send_request( agent_id, IdentityGet( - "did:iota:FFFAH6qct9KGQcSenG1iaw2Nj9jP7Zmug2zcmTpF4942" + "did:iota:rms:0xdfda8bcfb959c3e6ef261343c3e1a8310e9c8294eeafee326a4e96d65dbeaca0" .try_into() .unwrap(), ), diff --git a/identity_agent/src/tests/remote_account/error.rs b/identity_agent/src/tests/remote_account/error.rs index 25cff072e0..8b9c644112 100644 --- a/identity_agent/src/tests/remote_account/error.rs +++ b/identity_agent/src/tests/remote_account/error.rs @@ -7,12 +7,6 @@ pub(crate) enum RemoteAccountError { #[error("identity not found")] IdentityNotFound, - #[error("{0}")] - AccountError(String), -} - -impl From for RemoteAccountError { - fn from(err: identity_account::Error) -> Self { - Self::AccountError(err.to_string()) - } + #[error("placeholder DIDs cannot be managed")] + PlaceholderDID, } diff --git a/identity_agent/src/tests/remote_account/handler.rs b/identity_agent/src/tests/remote_account/handler.rs index d6e56b3032..aa19e85a82 100644 --- a/identity_agent/src/tests/remote_account/handler.rs +++ b/identity_agent/src/tests/remote_account/handler.rs @@ -4,11 +4,8 @@ use std::sync::Arc; use dashmap::DashMap; -use identity_account::account::Account; -use identity_account::account::AccountBuilder; -use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; -use tokio::sync::Mutex; +use identity_iota_core::IotaDID; +use identity_iota_core::IotaDocument; use crate::agent::Handler; use crate::agent::RequestContext; @@ -17,28 +14,31 @@ use crate::tests::remote_account::IdentityGet; use crate::tests::remote_account::IdentityList; use crate::tests::remote_account::RemoteAccountError; -/// A proof-of-concept implementation of a remote `Account` with very basic operations -/// and disabled tangle interaction. +/// A proof-of-concept implementation of a remote account +/// which holds and manages a collection of DID documents. #[derive(Debug, Clone)] pub(crate) struct RemoteAccount { - builder: Arc>, - accounts: Arc>, + documents: Arc>, } #[async_trait::async_trait] impl Handler for RemoteAccount { async fn handle(&self, _: RequestContext) -> Vec { - self.accounts.iter().map(|entry| entry.key().to_owned()).collect() + self.documents.iter().map(|entry| entry.key().to_owned()).collect() } } #[async_trait::async_trait] impl Handler for RemoteAccount { - async fn handle(&self, request: RequestContext) -> Result { - let account: Account = self.builder.lock().await.create_identity(request.input.into()).await?; - let doc = account.document().to_owned(); - self.accounts.insert(account.did().to_owned(), account); - Ok(doc) + async fn handle(&self, request: RequestContext) -> Result<(), RemoteAccountError> { + let document = request.input.0; + + if document.id().is_placeholder() { + return Err(RemoteAccountError::PlaceholderDID); + } + + self.documents.insert(document.id().to_owned(), document); + Ok(()) } } @@ -46,20 +46,17 @@ impl Handler for RemoteAccount { impl Handler for RemoteAccount { async fn handle(&self, request: RequestContext) -> Result { self - .accounts + .documents .get(&request.input.0) - .map(|account| account.document().to_owned()) + .map(|document| document.to_owned()) .ok_or(RemoteAccountError::IdentityNotFound) } } impl RemoteAccount { - pub(crate) fn new() -> identity_account::Result { - let builder: AccountBuilder = Account::builder().autopublish(false); - - Ok(Self { - builder: Arc::new(Mutex::new(builder)), - accounts: Arc::new(DashMap::new()), - }) + pub(crate) fn new() -> Self { + Self { + documents: Arc::new(DashMap::new()), + } } } diff --git a/identity_agent/src/tests/remote_account/requests.rs b/identity_agent/src/tests/remote_account/requests.rs index 6ad3b22fb2..c697fe9e67 100644 --- a/identity_agent/src/tests/remote_account/requests.rs +++ b/identity_agent/src/tests/remote_account/requests.rs @@ -1,9 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_account::types::IdentitySetup; -use identity_iota_core::did::IotaDID; -use identity_iota_core::document::IotaDocument; +use identity_iota_core::IotaDID; +use identity_iota_core::IotaDocument; use serde::Deserialize; use serde::Serialize; @@ -11,18 +10,12 @@ use crate::agent::Endpoint; use crate::agent::HandlerRequest; use crate::tests::remote_account::RemoteAccountError; -/// Can be sent to a `RemoteAccount` to instruct it to create an identity. -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub(crate) struct IdentityCreate; - -impl From for IdentitySetup { - fn from(_: IdentityCreate) -> Self { - IdentitySetup::default() - } -} +/// Can be sent to a `RemoteAccount` to instruct it to add a document. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct IdentityCreate(pub(crate) IotaDocument); impl HandlerRequest for IdentityCreate { - type Response = Result; + type Response = Result<(), RemoteAccountError>; fn endpoint() -> Endpoint { "remote_account/create".try_into().unwrap() diff --git a/identity_agent/src/tests/remote_account/tests.rs b/identity_agent/src/tests/remote_account/tests.rs index cc0cd7c71e..eaf9f25945 100644 --- a/identity_agent/src/tests/remote_account/tests.rs +++ b/identity_agent/src/tests/remote_account/tests.rs @@ -1,7 +1,9 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota_core::document::IotaDocument; +use identity_iota_core::IotaDID; +use identity_iota_core::IotaDocument; +use identity_iota_core::NetworkName; use crate::agent::Result as AgentResult; use crate::tests::default_listening_agent; @@ -17,7 +19,7 @@ async fn test_remote_account() -> AgentResult<()> { try_init_logger(); let (receiver, receiver_addrs, receiver_agent_id) = default_listening_agent(|mut builder| { - let remote_account = RemoteAccount::new().unwrap(); + let remote_account = RemoteAccount::new(); builder.attach::(remote_account.clone()); builder.attach::(remote_account.clone()); builder.attach::(remote_account); @@ -31,7 +33,12 @@ async fn test_remote_account() -> AgentResult<()> { .await .unwrap(); - let doc: IotaDocument = sender.send_request(receiver_agent_id, IdentityCreate).await?.unwrap(); + let doc = fake_document(); + + sender + .send_request(receiver_agent_id, IdentityCreate(doc.clone())) + .await? + .unwrap(); assert_eq!(sender.send_request(receiver_agent_id, IdentityList).await?.len(), 1); @@ -47,3 +54,13 @@ async fn test_remote_account() -> AgentResult<()> { Ok(()) } + +fn fake_document() -> IotaDocument { + let rand_bytes: [u8; 32] = rand::random(); + let network_name = NetworkName::try_from("iota").unwrap(); + let mut did = IotaDID::new(&rand_bytes, &network_name); + let mut doc = IotaDocument::new(&network_name); + // Let's act as if this was a published IotaDocument for testing purposes. + std::mem::swap(doc.core_document_mut().id_mut_unchecked(), &mut did); + doc +} diff --git a/identity_iota_core/src/did/iota_did.rs b/identity_iota_core/src/did/iota_did.rs index 4fc90f170c..539b9511d0 100644 --- a/identity_iota_core/src/did/iota_did.rs +++ b/identity_iota_core/src/did/iota_did.rs @@ -44,7 +44,10 @@ impl IotaDID { /// The default network name (`"iota"`). pub const DEFAULT_NETWORK: &'static str = "iota"; - // The length of an Alias ID, which is a BLAKE2b-256 hash (32-bytes). + /// The tag of the placeholder DID. + pub const PLACEHOLDER_TAG: &'static str = "0x0000000000000000000000000000000000000000000000000000000000000000"; + + /// The length of an Alias ID, which is a BLAKE2b-256 hash (32-bytes). pub(crate) const TAG_BYTES_LEN: usize = 32; // =========================================================================== @@ -90,10 +93,26 @@ impl IotaDID { /// # /// let placeholder = IotaDID::placeholder(&NetworkName::try_from("smr").unwrap()); /// assert_eq!(placeholder.as_str(), "did:iota:smr:0x0000000000000000000000000000000000000000000000000000000000000000"); + /// assert!(placeholder.is_placeholder()); pub fn placeholder(network_name: &NetworkName) -> Self { Self::new(&[0; 32], network_name) } + /// Returns whether this is the placeholder DID. + /// + /// # Example + /// + /// ``` + /// # use identity_did::did::DID; + /// # use identity_iota_core::NetworkName; + /// # use identity_iota_core::IotaDID; + /// # + /// let placeholder = IotaDID::placeholder(&NetworkName::try_from("smr").unwrap()); + /// assert!(placeholder.is_placeholder()); + pub fn is_placeholder(&self) -> bool { + self.tag() == Self::PLACEHOLDER_TAG + } + /// Parses an [`IotaDID`] from the given `input`. /// /// # Errors @@ -324,8 +343,6 @@ mod tests { // Reusable constants and statics // =========================================================================================================================== - const PLACEHOLDER_TAG: &str = "0x0000000000000000000000000000000000000000000000000000000000000000"; - // obtained AliasID from a valid OutputID string // output_id copied from https://github.com/iotaledger/bee/blob/30cab4f02e9f5d72ffe137fd9eb09723b4f0fdb6/bee-block/tests/output_id.rs // value of AliasID computed from AliasId::from(OutputId).to_string() @@ -359,7 +376,7 @@ mod tests { let valid_strings: Vec = VALID_NETWORK_NAMES .iter() .flat_map(|network| { - [VALID_ALIAS_ID_STR, PLACEHOLDER_TAG] + [VALID_ALIAS_ID_STR, IotaDID::PLACEHOLDER_TAG] .iter() .map(move |tag| network_tag_to_did(network, tag)) }) @@ -505,7 +522,7 @@ mod tests { fn placeholder_produces_a_did_with_expected_string_representation() { assert_eq!( IotaDID::placeholder(&NetworkName::try_from(IotaDID::DEFAULT_NETWORK).unwrap()).as_str(), - format!("did:{}:{}", IotaDID::METHOD, PLACEHOLDER_TAG) + format!("did:{}:{}", IotaDID::METHOD, IotaDID::PLACEHOLDER_TAG) ); for name in VALID_NETWORK_NAMES @@ -516,7 +533,7 @@ mod tests { let did: IotaDID = IotaDID::placeholder(&network_name); assert_eq!( did.as_str(), - format!("did:{}:{}:{}", IotaDID::METHOD, name, PLACEHOLDER_TAG) + format!("did:{}:{}:{}", IotaDID::METHOD, name, IotaDID::PLACEHOLDER_TAG) ); } } @@ -583,7 +600,7 @@ mod tests { )); }; - execute_assertions(PLACEHOLDER_TAG); + execute_assertions(IotaDID::PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } @@ -730,7 +747,7 @@ mod tests { assert_eq!(did.network_str(), "custom"); }; - execute_assertions(PLACEHOLDER_TAG); + execute_assertions(IotaDID::PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } @@ -760,7 +777,7 @@ mod tests { .unwrap(); assert_eq!(did.tag(), valid_alias_id); }; - execute_assertions(PLACEHOLDER_TAG); + execute_assertions(IotaDID::PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } @@ -831,7 +848,7 @@ mod tests { )) .is_ok()); }; - execute_assertions(PLACEHOLDER_TAG); + execute_assertions(IotaDID::PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } @@ -850,7 +867,7 @@ mod tests { assert_eq!(did_url.query(), Some("diff=true")); assert_eq!(did_url.fragment(), Some("foo")); }; - execute_assertions(PLACEHOLDER_TAG); + execute_assertions(IotaDID::PLACEHOLDER_TAG); execute_assertions(VALID_ALIAS_ID_STR); } } From 0fe197384814fc5e1e7c0e915db06475f1554257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Mon, 21 Nov 2022 11:32:28 +0100 Subject: [PATCH 3/6] Remove legacy account examples (#1084) --- bindings/wasm/cypress/e2e/.gitkeep | 0 .../wasm/cypress/e2e/createIdentity.cy.js | 25 -- bindings/wasm/cypress/e2e/keyExchange.cy.js | 25 -- .../wasm/cypress/e2e/manipulateIdentity.cy.js | 25 -- bindings/wasm/cypress/e2e/privateTangle.cy.js | 31 -- .../wasm/cypress/e2e/resolveHistory.cy.js | 25 -- .../wasm/cypress/e2e/resolveIdentity.cy.js | 25 -- bindings/wasm/examples-account/README.md | 55 --- bindings/wasm/examples-account/src/config.ts | 57 --- .../wasm/examples-account/src/create_did.ts | 31 -- .../wasm/examples-account/src/create_vc.ts | 85 ---- .../wasm/examples-account/src/create_vp.ts | 188 --------- .../examples-account/src/custom_storage.ts | 246 ------------ .../wasm/examples-account/src/encryption.ts | 83 ---- bindings/wasm/examples-account/src/lazy.ts | 53 --- .../examples-account/src/manipulate_did.ts | 57 --- .../src/multiple_identities.ts | 52 --- bindings/wasm/examples-account/src/node.ts | 61 --- .../wasm/examples-account/src/revoke_vc.ts | 137 ------- bindings/wasm/examples-account/src/signing.ts | 83 ---- .../wasm/examples-account/src/tests/config.ts | 8 - .../examples-account/src/tests/create_did.ts | 8 - .../examples-account/src/tests/create_vc.ts | 8 - .../examples-account/src/tests/create_vp.ts | 8 - .../examples-account/src/tests/encryption.ts | 8 - .../wasm/examples-account/src/tests/lazy.ts | 8 - .../src/tests/manipulate_did.ts | 8 - .../src/tests/multiple_identities.ts | 8 - .../examples-account/src/tests/revoke_vc.ts | 8 - .../examples-account/src/tests/signing.ts | 8 - .../examples-account/src/tests/unchecked.ts | 8 - .../wasm/examples-account/src/unchecked.ts | 45 --- bindings/wasm/package-lock.json | 373 ------------------ bindings/wasm/package.json | 4 - bindings/wasm/tests/legacy/account.ts | 137 ------- 35 files changed, 1991 deletions(-) create mode 100644 bindings/wasm/cypress/e2e/.gitkeep delete mode 100644 bindings/wasm/cypress/e2e/createIdentity.cy.js delete mode 100644 bindings/wasm/cypress/e2e/keyExchange.cy.js delete mode 100644 bindings/wasm/cypress/e2e/manipulateIdentity.cy.js delete mode 100644 bindings/wasm/cypress/e2e/privateTangle.cy.js delete mode 100644 bindings/wasm/cypress/e2e/resolveHistory.cy.js delete mode 100644 bindings/wasm/cypress/e2e/resolveIdentity.cy.js delete mode 100644 bindings/wasm/examples-account/README.md delete mode 100644 bindings/wasm/examples-account/src/config.ts delete mode 100644 bindings/wasm/examples-account/src/create_did.ts delete mode 100644 bindings/wasm/examples-account/src/create_vc.ts delete mode 100644 bindings/wasm/examples-account/src/create_vp.ts delete mode 100644 bindings/wasm/examples-account/src/custom_storage.ts delete mode 100644 bindings/wasm/examples-account/src/encryption.ts delete mode 100644 bindings/wasm/examples-account/src/lazy.ts delete mode 100644 bindings/wasm/examples-account/src/manipulate_did.ts delete mode 100644 bindings/wasm/examples-account/src/multiple_identities.ts delete mode 100644 bindings/wasm/examples-account/src/node.ts delete mode 100644 bindings/wasm/examples-account/src/revoke_vc.ts delete mode 100644 bindings/wasm/examples-account/src/signing.ts delete mode 100644 bindings/wasm/examples-account/src/tests/config.ts delete mode 100644 bindings/wasm/examples-account/src/tests/create_did.ts delete mode 100644 bindings/wasm/examples-account/src/tests/create_vc.ts delete mode 100644 bindings/wasm/examples-account/src/tests/create_vp.ts delete mode 100644 bindings/wasm/examples-account/src/tests/encryption.ts delete mode 100644 bindings/wasm/examples-account/src/tests/lazy.ts delete mode 100644 bindings/wasm/examples-account/src/tests/manipulate_did.ts delete mode 100644 bindings/wasm/examples-account/src/tests/multiple_identities.ts delete mode 100644 bindings/wasm/examples-account/src/tests/revoke_vc.ts delete mode 100644 bindings/wasm/examples-account/src/tests/signing.ts delete mode 100644 bindings/wasm/examples-account/src/tests/unchecked.ts delete mode 100644 bindings/wasm/examples-account/src/unchecked.ts delete mode 100644 bindings/wasm/tests/legacy/account.ts diff --git a/bindings/wasm/cypress/e2e/.gitkeep b/bindings/wasm/cypress/e2e/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/wasm/cypress/e2e/createIdentity.cy.js b/bindings/wasm/cypress/e2e/createIdentity.cy.js deleted file mode 100644 index 4528cc8ad2..0000000000 --- a/bindings/wasm/cypress/e2e/createIdentity.cy.js +++ /dev/null @@ -1,25 +0,0 @@ -import { - createIdentity, - defaultClientConfig, - initIdentity, -} from '../../examples/dist/web' - -describe( - "createIdentity", - { - defaultCommandTimeout: 1000*60*3, // 3 minutes to account for spurious network delays - }, - () => { - before(async () => { - // The working directory is under __cypress at test runtime, so we need to go up one more level than usual - await initIdentity('../../../examples/dist/identity_wasm_bg.wasm'); - - // NOTE: `cy.wrap(defaultClientConfig()).as('config')` does not always work to make the config available - // from the shared context as `this.config` because it has a race condition with initializing the wasm. - // So call `defaultClientConfig()` manually for now. - }); - it("Create Identity", async () => { - await createIdentity(defaultClientConfig()); - }); - } -); diff --git a/bindings/wasm/cypress/e2e/keyExchange.cy.js b/bindings/wasm/cypress/e2e/keyExchange.cy.js deleted file mode 100644 index 1b8d348c4d..0000000000 --- a/bindings/wasm/cypress/e2e/keyExchange.cy.js +++ /dev/null @@ -1,25 +0,0 @@ -import { - keyExchange, - defaultClientConfig, - initIdentity, -} from '../../examples/dist/web' - -describe( - "keyExchange", - { - defaultCommandTimeout: 1000*60*3, // 3 minutes to account for spurious network delays - }, - () => { - before(async () => { - // The working directory is under __cypress at test runtime, so we need to go up one more level than usual - await initIdentity('../../../examples/dist/identity_wasm_bg.wasm'); - - // NOTE: `cy.wrap(defaultClientConfig()).as('config')` does not always work to make the config available - // from the shared context as `this.config` because it has a race condition with initializing the wasm. - // So call `defaultClientConfig()` manually for now. - }); - it("Key Exchange", async () => { - await keyExchange(defaultClientConfig()); - }); - } -); diff --git a/bindings/wasm/cypress/e2e/manipulateIdentity.cy.js b/bindings/wasm/cypress/e2e/manipulateIdentity.cy.js deleted file mode 100644 index 346c9873b5..0000000000 --- a/bindings/wasm/cypress/e2e/manipulateIdentity.cy.js +++ /dev/null @@ -1,25 +0,0 @@ -import { - defaultClientConfig, - initIdentity, - manipulateIdentity, -} from '../../examples/dist/web' - -describe( - "manipulateIdentity", - { - defaultCommandTimeout: 1000*60*3, // 3 minutes to account for spurious network delays - }, - () => { - before(async () => { - // The working directory is under __cypress at test runtime, so we need to go up one more level than usual - await initIdentity('../../../examples/dist/identity_wasm_bg.wasm'); - - // NOTE: `cy.wrap(defaultClientConfig()).as('config')` does not always work to make the config available - // from the shared context as `this.config` because it has a race condition with initializing the wasm. - // So call `defaultClientConfig()` manually for now. - }); - it("Manipulate Identity", async () => { - await manipulateIdentity(defaultClientConfig()); - }); - } -); diff --git a/bindings/wasm/cypress/e2e/privateTangle.cy.js b/bindings/wasm/cypress/e2e/privateTangle.cy.js deleted file mode 100644 index 1e2e4f076c..0000000000 --- a/bindings/wasm/cypress/e2e/privateTangle.cy.js +++ /dev/null @@ -1,31 +0,0 @@ -import { - initIdentity, - privateTangle, -} from '../../examples/dist/web' - -describe( - "privateTangle", - { - defaultCommandTimeout: 1000*60*3, // 3 minutes to account for spurious network delays - }, - () => { - before(async () => { - // The working directory is under __cypress at test runtime, so we need to go up one more level than usual - await initIdentity('../../../examples/dist/identity_wasm_bg.wasm'); - - // NOTE: `cy.wrap(defaultClientConfig()).as('config')` does not always work to make the config available - // from the shared context as `this.config` because it has a race condition with initializing the wasm. - // So call `defaultClientConfig()` manually for now. - }); - it("Private Tangle", async function () { - try { - await privateTangle("http://127.0.0.1:1111/") - throw new Error("Did not throw.") - } catch (err) { - // Example is expected to throw an error because no private Tangle is running - expect(err.name).to.eq("ClientError") - expect(err.message).to.contain("error sending request") - } - }); - } -); diff --git a/bindings/wasm/cypress/e2e/resolveHistory.cy.js b/bindings/wasm/cypress/e2e/resolveHistory.cy.js deleted file mode 100644 index 4c48b05abf..0000000000 --- a/bindings/wasm/cypress/e2e/resolveHistory.cy.js +++ /dev/null @@ -1,25 +0,0 @@ -import { - defaultClientConfig, - initIdentity, - resolveHistory -} from '../../examples/dist/web' - -describe( - "resolveHistory", - { - defaultCommandTimeout: 1000*60*3, // 3 minutes to account for spurious network delays - }, - () => { - before(async () => { - // The working directory is under __cypress at test runtime, so we need to go up one more level than usual - await initIdentity('../../../examples/dist/identity_wasm_bg.wasm'); - - // NOTE: `cy.wrap(defaultClientConfig()).as('config')` does not always work to make the config available - // from the shared context as `this.config` because it has a race condition with initializing the wasm. - // So call `defaultClientConfig()` manually for now. - }); - it("Resolve History", async () => { - await resolveHistory(defaultClientConfig()); - }); - } -); diff --git a/bindings/wasm/cypress/e2e/resolveIdentity.cy.js b/bindings/wasm/cypress/e2e/resolveIdentity.cy.js deleted file mode 100644 index 9e699fce43..0000000000 --- a/bindings/wasm/cypress/e2e/resolveIdentity.cy.js +++ /dev/null @@ -1,25 +0,0 @@ -import { - defaultClientConfig, - initIdentity, - resolveIdentity, -} from '../../examples/dist/web' - -describe( - "resolveIdentity", - { - defaultCommandTimeout: 1000*60*3, // 3 minutes to account for spurious network delays - }, - () => { - before(async () => { - // The working directory is under __cypress at test runtime, so we need to go up one more level than usual - await initIdentity('../../../examples/dist/identity_wasm_bg.wasm'); - - // NOTE: `cy.wrap(defaultClientConfig()).as('config')` does not always work to make the config available - // from the shared context as `this.config` because it has a race condition with initializing the wasm. - // So call `defaultClientConfig()` manually for now. - }); - it("Resolution", async () => { - await resolveIdentity(defaultClientConfig()); - }); - } -); diff --git a/bindings/wasm/examples-account/README.md b/bindings/wasm/examples-account/README.md deleted file mode 100644 index d8f4c4e416..0000000000 --- a/bindings/wasm/examples-account/README.md +++ /dev/null @@ -1,55 +0,0 @@ -![banner](./../../../documentation/static/img/Banner/banner_identity.svg) - - -## IOTA Identity Account Examples - -This folder provides code examples for you to learn how the IOTA Identity WASM bindings for the Account can be used in JavaScript/Typescript. - -The examples are written in Typescript and can be run independently with Node.js. - -### Node.js - -In order to run the examples in Node.js make sure to install the dependencies: - -```bash -npm install -``` - - -And build the bindings: - -```bash -npm run build -``` - -Then run the example using: - -```bash - -npm run example:account -- -``` - -For instance, to run the `create_did` example use: - -```bash -npm run example:account -- create_did -``` - -| # | Name | Details | -| -------- | -------- | -------- | -|1| [create_did](src/create_did.ts)| A basic example that generates and publishes a DID Document, the fundamental building block for decentralized identity. | -|2| [config](src/config.ts) | How to configure the account to work with different networks and other settings. | -|3| [manipulate_did](src/manipulate_did.ts)| How to manipulate a DID Document by adding/removing Verification Methods and Services. | -|4| [lazy](src/lazy.ts)| How to take control over publishing DID updates manually, instead of the default automated behavior. | -|5| [signing](src/signing.ts) | Using a DID to sign arbitrary statements and validating them. | -|6| [create_vc](src/create_vc.ts) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (VC) specifying claims about the subject, and verifies it.| -|7| [create_vp](src/create_vp.ts) | Create a Verifiable Presentation, the data model for sharing VCs, out of a Verifiable Credential and verifies it. -|8| [revoke_vc](src/revoke_vc.ts) | Remove a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. -|9| [multiple_identities](src/multiple_identities.ts) | How to create multiple identities from a builder and how to load existing identities into an account. | -|10| [unchecked](src/unchecked.ts) | How to update the custom properties of a DID document directly by using the account's unchecked methods. | -|11| [custom_storage](src/custom_storage.ts) | Example implementation of a custom storage and testing it with the storage test suite. | -|12| [encryption](src/encryption.ts) | Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange by encrypting and decrypting data with a shared key. | - -## Browser - -Although the examples should work in browser environment, we don't provide a browser project as for now. diff --git a/bindings/wasm/examples-account/src/config.ts b/bindings/wasm/examples-account/src/config.ts deleted file mode 100644 index c2fd58efe1..0000000000 --- a/bindings/wasm/examples-account/src/config.ts +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { AccountBuilder, AutoSave, ExplorerUrl, Network, Storage } from "../../node"; - -/** - * This example demonstrates some of the configuration options for the account. - */ -async function config(storage?: Storage) { - // Set-up for a private Tangle - // You can use https://github.com/iotaledger/one-click-tangle for a local setup. - // The `network_name` needs to match the id of the network or a part of it. - // As an example we are treating the devnet as a private tangle, so we use `dev`. - // When running the local setup, we can use `tangle` since the id of the one-click - // private tangle is `private-tangle`, but we can only use 6 characters. - // Keep in mind, there are easier ways to change to devnet via `Network::Devnet` - const network_name = "dev"; - let network = Network.tryFromName(network_name); - - // If you deployed an explorer locally this would usually be `http://127.0.0.1:8082` - const explorer = ExplorerUrl.parse("https://explorer.iota.org/devnet"); - - // In a locally running one-click tangle, this would usually be `http://127.0.0.1:14265` - let private_node_url = "https://api.lb-0.h.chrysalis-devnet.iota.cafe"; - - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - const builder = new AccountBuilder({ - // `AutoSave.never()` never auto-saves, relies on the storage drop save. - // `AutoSave.every()` saves immediately after every action, - autosave: AutoSave.batch(10), // saves after every 10 actions. - autopublish: true, // publish to the tangle automatically on every update - clientConfig: { - network: network, - primaryNode: { url: private_node_url }, - }, - storage, - }); - - // Create an identity and publish it. - // The created DID will use the network name configured for the client. - try { - const account = await builder.createIdentity(); - const did = account.did(); - - // Prints the Identity Resolver Explorer URL. - // The entire history can be observed on this page by clicking "Loading History". - console.log(`[Example] Explore the DID Document = ${explorer.resolverUrl(did)}`); - } catch (e) { - if (e instanceof Error) { - console.log(`[Example] Error: ${e.message}`); - } - console.log(`[Example] Is your Tangle node listening on ${private_node_url}?`); - } -} - -export { config }; diff --git a/bindings/wasm/examples-account/src/create_did.ts b/bindings/wasm/examples-account/src/create_did.ts deleted file mode 100644 index 9f35977fa4..0000000000 --- a/bindings/wasm/examples-account/src/create_did.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { AccountBuilder, ExplorerUrl, Storage } from "../../node"; - -/** - * This example shows a basic introduction on how to create a basic DID Document and upload it to the Tangle - * using the Account. - */ -async function createIdentity(storage?: Storage) { - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let builder = new AccountBuilder({ - storage, - }); - let account = await builder.createIdentity(); - - // Retrieve the DID of the newly created identity. - let did = account.did(); - - // Print the DID of the created Identity. - console.log(did.toString()); - - // Print the local state of the DID Document - console.log(account.document()); - - // Print the Explorer URL for the DID. - console.log(`Explorer Url:`, ExplorerUrl.mainnet().resolverUrl(did)); -} - -export { createIdentity }; diff --git a/bindings/wasm/examples-account/src/create_vc.ts b/bindings/wasm/examples-account/src/create_vc.ts deleted file mode 100644 index 91a81a0265..0000000000 --- a/bindings/wasm/examples-account/src/create_vc.ts +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - AccountBuilder, - Credential, - CredentialValidationOptions, - CredentialValidator, - FailFast, - MethodContent, - ProofOptions, - Storage, -} from "./../../node/identity_wasm.js"; - -/** - This example shows how to create a Verifiable Credential and validate it. - In this example, alice takes the role of the subject, while we also have an issuer. - The issuer signs a UniversityDegreeCredential type verifiable credential with Alice's name and DID. - This Verifiable Credential can be verified by anyone, allowing Alice to take control of it and share it with whomever they please. - **/ -async function createVC(storage?: Storage) { - const builder = new AccountBuilder({ - storage, - }); - - // Create an identity for the issuer. - const issuer = await builder.createIdentity(); - - // Add verification method to the issuer. - await issuer.createMethod({ - content: MethodContent.GenerateEd25519(), - fragment: "#issuerKey", - }); - - // Create an identity for the holder, in this case also the subject. - const alice = await builder.createIdentity(); - - // Create a credential subject indicating the degree earned by Alice, linked to their DID. - const subject = { - id: alice.document().id(), - name: "Alice", - degreeName: "Bachelor of Science and Arts", - degreeType: "BachelorDegree", - GPA: "4.0", - }; - - // Create an unsigned `UniversityDegree` credential for Alice - const unsignedVc = new Credential({ - id: "https://example.edu/credentials/3732", - type: "UniversityDegreeCredential", - issuer: issuer.document().id(), - credentialSubject: subject, - }); - - // Created a signed credential by the issuer. - const signedVc = await issuer.createSignedCredential( - "#issuerKey", - unsignedVc, - ProofOptions.default(), - ); - - // Before sending this credential to the holder the issuer wants to validate that some properties - // of the credential satisfy their expectations. - - // Validate the credential's signature, the credential's semantic structure, - // check that the issuance date is not in the future and that the expiration date is not in the past. - // TODO: uncomment when ported to Stardust. - // CredentialValidator.validate( - // signedVc, - // issuer.document(), - // CredentialValidationOptions.default(), - // FailFast.AllErrors - // ); - - // Since `validate` did not throw any errors we know that the credential was successfully validated. - console.log(`VC successfully validated`); - - // The issuer is now sure that the credential they are about to issue satisfies their expectations. - // The credential is then serialized to JSON and transmitted to the holder in a secure manner. - // Note that the credential is NOT published to the IOTA Tangle. It is sent and stored off-chain. - const credentialJSON = signedVc.toJSON(); - return { alice, issuer, credentialJSON }; -} - -export { createVC }; diff --git a/bindings/wasm/examples-account/src/create_vp.ts b/bindings/wasm/examples-account/src/create_vp.ts deleted file mode 100644 index 558bdb3432..0000000000 --- a/bindings/wasm/examples-account/src/create_vp.ts +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - AccountBuilder, - Credential, - CredentialValidationOptions, - Duration, - FailFast, - MethodContent, - Network, - Presentation, - PresentationValidationOptions, - ProofOptions, - Resolver, - Storage, - SubjectHolderRelationship, - Timestamp, - VerifierOptions, -} from "./../../node/identity_wasm.js"; - -/** - This example shows how to create a Verifiable Presentation and validate it. - A Verifiable Presentation is the format in which a (collection of) Verifiable Credential(s) gets shared. - It is signed by the subject, to prove control over the Verifiable Credential with a nonce or timestamp. - **/ -async function createVP(storage?: Storage) { - // =========================================================================== - // Step 1: Create identities for the issuer and the holder. - // =========================================================================== - - // Create Account builder. - const builder = new AccountBuilder({ - storage, - }); - - // Create an identity for the issuer. - const issuer = await builder.createIdentity(); - - // Add a dedicated verification method to the issuer, with which to sign credentials. - await issuer.createMethod({ - content: MethodContent.GenerateEd25519(), - fragment: "issuerKey", - }); - - // Create an identity for the holder, in this case also the subject. - const alice = await builder.createIdentity(); - - // Add verification method to the holder. - await alice.createMethod({ - content: MethodContent.GenerateEd25519(), - fragment: "aliceKey", - }); - - // =========================================================================== - // Step 2: Issuer creates and signs a Verifiable Credential. - // =========================================================================== - - // Create a credential subject indicating the degree earned by Alice, linked to their DID. - const subject = { - id: alice.document().id(), - name: "Alice", - degreeName: "Bachelor of Science and Arts", - degreeType: "BachelorDegree", - GPA: "4.0", - }; - - // Create an unsigned `UniversityDegree` credential for Alice - const unsignedVc = new Credential({ - id: "https://example.edu/credentials/3732", - type: "UniversityDegreeCredential", - issuer: issuer.document().id(), - credentialSubject: subject, - }); - - // Created a signed credential by the issuer. - const signedVc = await issuer.createSignedCredential( - "#issuerKey", - unsignedVc, - ProofOptions.default(), - ); - - // =========================================================================== - // Step 3: Issuer sends the Verifiable Credential to the holder. - // =========================================================================== - - // The credential is then serialized to JSON and transmitted to the holder in a secure manner. - // Note that the credential is NOT published to the IOTA Tangle. It is sent and stored off-chain. - const signedVcJson = signedVc.toJSON(); - console.log(`Credential JSON >`, JSON.stringify(signedVcJson, null, 2)); - - // =========================================================================== - // Step 4: Verifier sends the holder a challenge and requests a signed Verifiable Presentation. - // =========================================================================== - - // A unique random challenge generated by the requester per presentation can mitigate replay attacks. - const challenge = "475a7984-1bb5-4c4c-a56f-822bccd46440"; - - // The verifier and holder also agree that the signature should have an expiry date - // 10 minutes from now. - const expires = Timestamp.nowUTC().checkedAdd(Duration.minutes(10)); - - // =========================================================================== - // Step 5: Holder creates a verifiable presentation from the issued credential for the verifier to validate. - // =========================================================================== - - // Deserialize the credential. - const receivedVc = Credential.fromJSON(signedVcJson); - - // Create a Verifiable Presentation from the Credential - const unsignedVp = new Presentation({ - holder: alice.did(), - verifiableCredential: receivedVc, - }); - - // Sign the verifiable presentation using the holder's verification method - // and include the requested challenge and expiry timestamp. - const signedVp = await alice.createSignedPresentation( - "#aliceKey", - unsignedVp, - new ProofOptions({ - challenge: challenge, - expires, - }), - ); - - // =========================================================================== - // Step 6: Holder sends a verifiable presentation to the verifier. - // =========================================================================== - - // Convert the Verifiable Presentation to JSON to send it to the verifier. - const signedVpJSON = signedVp.toJSON(); - - // =========================================================================== - // Step 7: Verifier receives the Verifiable Presentation and verifies it. - // =========================================================================== - - // Deserialize the presentation from the holder. - const presentation = Presentation.fromJSON(signedVpJSON); - - // The verifier wants the following requirements to be satisfied: - // - Signature verification (including checking the requested challenge to mitigate replay attacks) - // - Presentation validation must fail if credentials expiring within the next 10 hours are encountered - // - The presentation holder must always be the subject, regardless of the presence of the nonTransferable property - // - The issuance date must not be in the future. - - // Declare that the challenge must match our expectation: - const presentationVerifierOptions = new VerifierOptions({ - challenge: "475a7984-1bb5-4c4c-a56f-822bccd46440", - allowExpired: false, - }); - - // Declare that any credential contained in the presentation are not allowed to expire within the next 10 hours: - const earliestExpiryDate = Timestamp.nowUTC().checkedAdd(Duration.hours(10)); - const credentialValidationOptions = new CredentialValidationOptions({ - earliestExpiryDate: earliestExpiryDate, - }); - - // Declare that the presentation holder's DID must match the subject ID on all credentials in the presentation. - const subjectHolderRelationship = SubjectHolderRelationship.AlwaysSubject; - - const presentationValidationOptions = new PresentationValidationOptions({ - sharedValidationOptions: credentialValidationOptions, - presentationVerifierOptions: presentationVerifierOptions, - subjectHolderRelationship: subjectHolderRelationship, - }); - - // In order to validate presentations and credentials one needs to resolve the DID Documents of - // the presentation holder and of credential issuers. This is something the `Resolver` can help with. - const resolver = new Resolver(); - - // Validate the presentation and all the credentials included in it according to the validation options - // TODO: uncomment when ported to Stardust. - // await resolver.verifyPresentation( - // presentation, - // presentationValidationOptions, - // FailFast.FirstError - // ); - - // Since no errors were thrown by `verifyPresentation` we know that the validation was successful. - console.log(`VP successfully validated`); - - // Note that the `verifyPresentation` method we called automatically resolved all DID Documents that are necessary to validate the presentation. - // It is also possible to supply extra arguments to avoid some resolutions if one already has up-to-date resolved documents of - // either the holder or issuers (see the method's documentation). -} - -export { createVP }; diff --git a/bindings/wasm/examples-account/src/custom_storage.ts b/bindings/wasm/examples-account/src/custom_storage.ts deleted file mode 100644 index b595645dc7..0000000000 --- a/bindings/wasm/examples-account/src/custom_storage.ts +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - CekAlgorithm, - DID, - Ed25519, - EncryptedData, - EncryptionAlgorithm, - KeyLocation, - KeyPair, - KeyType, - Signature, - Storage, - StorageTestSuite, -} from "../../node/identity_wasm.js"; - -/** An insecure, in-memory `Storage` implementation that serves as an example. -This can be passed to the `AccountBuilder` to create accounts with this as the storage. */ -// Refer to the `Storage` interface docs for high-level documentation of the individual methods. -export class MemStore implements Storage { - // We use strings as keys rather than DIDs or KeyLocations because Maps use - // referential equality for object keys, and thus a primitive type needs to be used instead. - - // The map from DIDs to state. - private _blobs: Map; - // Map of DID state blobs. - private _vaults: Map>; - - /** Creates a new, empty `MemStore` instance. */ - constructor() { - this._blobs = new Map(); - this._vaults = new Map(); - } - - public async didCreate(network: string, fragment: string, privateKey?: Uint8Array): Promise<[DID, KeyLocation]> { - // Extract a `KeyPair` from the passed private key or generate a new one. - // For `did_create` we can assume the `KeyType` to be `Ed25519` because - // that is the only currently available signature type. - let keyPair; - if (privateKey) { - keyPair = KeyPair.tryFromPrivateKeyBytes(KeyType.Ed25519, privateKey); - } else { - keyPair = new KeyPair(KeyType.Ed25519); - } - - // We create the location at which the key pair will be stored. - // Most notably, this uses the public key as an input. - const keyLocation: KeyLocation = new KeyLocation(KeyType.Ed25519, fragment, keyPair.public()); - - // Next we use the public key to derive the initial DID. - const did: DID = new DID(keyPair.public(), network); - - // We use the vaults as the index of DIDs stored in this storage instance. - // If the DID already exists, we need to return an error. We don't want to overwrite an existing DID. - if (this._vaults.has(did.toString())) { - throw new Error("identity already exists"); - } - - const vault = this._vaults.get(did.toString()); - - // Get the existing vault and insert the key pair, - // or insert a new vault with the key pair. - if (vault) { - vault.set(keyLocation.canonical(), keyPair); - } else { - const newVault = new Map([[keyLocation.canonical(), keyPair]]); - this._vaults.set(did.toString(), newVault); - } - - return [did, keyLocation]; - } - - public async didPurge(did: DID): Promise { - // This method is supposed to be idempotent, - // so we only need to do work if the DID still exists. - // The return value signals whether the DID was actually removed during this operation. - if (this._vaults.has(did.toString())) { - this._blobs.delete(did.toString()); - this._vaults.delete(did.toString()); - return true; - } - - return false; - } - - public async didExists(did: DID): Promise { - return this._vaults.has(did.toString()); - } - - public async didList(): Promise> { - // Get all keys from the vaults and parse them into DIDs. - return Array.from(this._vaults.keys()).map((did) => DID.parse(did)); - } - - public async keyGenerate(did: DID, keyType: KeyType, fragment: string): Promise { - // Generate a new key pair with the given key type. - const keyPair: KeyPair = new KeyPair(keyType); - // Derive the key location from the fragment and public key and set the `KeyType` of the location. - const keyLocation: KeyLocation = new KeyLocation(KeyType.Ed25519, fragment, keyPair.public()); - - const vault = this._vaults.get(did.toString()); - - // Get the existing vault and insert the key pair, - // or insert a new vault with the key pair. - if (vault) { - vault.set(keyLocation.canonical(), keyPair); - } else { - const newVault = new Map([[keyLocation.canonical(), keyPair]]); - this._vaults.set(did.toString(), newVault); - } - - // Return the location at which the key was generated. - return keyLocation; - } - - public async keyInsert(did: DID, keyLocation: KeyLocation, privateKey: Uint8Array): Promise { - // Reconstruct the key pair from the given private key with the location's key type. - const keyPair: KeyPair = KeyPair.tryFromPrivateKeyBytes(keyLocation.keyType(), privateKey); - - // Get the vault for the given DID. - const vault = this._vaults.get(did.toString()); - - // Get the existing vault and insert the key pair, - // or insert a new vault with the key pair. - if (vault) { - vault.set(keyLocation.canonical(), keyPair); - } else { - const newVault = new Map([[keyLocation.canonical(), keyPair]]); - this._vaults.set(did.toString(), newVault); - } - } - - public async keyExists(did: DID, keyLocation: KeyLocation): Promise { - // Get the vault for the given DID. - const vault = this._vaults.get(did.toString()); - - // Within the DID vault, check for existence of the given location. - if (vault) { - return vault.has(keyLocation.canonical()); - } else { - return false; - } - } - - public async keyPublic(did: DID, keyLocation: KeyLocation): Promise { - // Get the vault for the given DID. - const vault = this._vaults.get(did.toString()); - - // Return the public key or an error if the vault or key does not exist. - if (vault) { - const keyPair: KeyPair | undefined = vault.get(keyLocation.canonical()); - if (keyPair) { - return keyPair.public(); - } else { - throw new Error("Key location not found"); - } - } else { - throw new Error("DID not found"); - } - } - - public async keyDelete(did: DID, keyLocation: KeyLocation): Promise { - // Get the vault for the given DID. - const vault = this._vaults.get(did.toString()); - - // This method is supposed to be idempotent, so we delete the key - // if it exists and return whether it was actually deleted during this operation. - if (vault) { - return vault.delete(keyLocation.canonical()); - } else { - return false; - } - } - - public async keySign(did: DID, keyLocation: KeyLocation, data: Uint8Array): Promise { - if (keyLocation.keyType() !== KeyType.Ed25519) { - throw new Error("Unsupported Method"); - } - - // Get the vault for the given DID. - const vault = this._vaults.get(did.toString()); - - if (vault) { - const keyPair: KeyPair | undefined = vault.get(keyLocation.canonical()); - - if (keyPair) { - // Use the `Ed25519` API to sign the given data with the private key. - const signature: Uint8Array = Ed25519.sign(data, keyPair.private()); - // Construct a new `Signature` wrapper with the returned signature bytes. - return new Signature(signature); - } else { - throw new Error("Key location not found"); - } - } else { - throw new Error("DID not found"); - } - } - - public async dataEncrypt( - did: DID, - plaintext: Uint8Array, - associatedData: Uint8Array, - encryptionAlgorithm: EncryptionAlgorithm, - cekAlgorithm: CekAlgorithm, - publicKey: Uint8Array, - ): Promise { - throw new Error("not yet implemented"); - } - - public async dataDecrypt( - did: DID, - data: EncryptedData, - encryptionAlgorithm: EncryptionAlgorithm, - cekAlgorithm: CekAlgorithm, - privateKey: KeyLocation, - ): Promise { - throw new Error("not yet implemented"); - } - - public async blobGet(did: DID): Promise { - // Lookup the state of the given DID. - return this._blobs.get(did.toString()); - } - - public async blobSet(did: DID, value: Uint8Array): Promise { - // Set the state of the given DID. - this._blobs.set(did.toString(), value); - } - - public async flushChanges(): Promise { - // The MemStore doesn't need to flush changes to disk or any other persistent store, - // which is why this function does nothing. - } -} - -export async function storageTestSuite() { - await StorageTestSuite.didCreateGenerateKeyTest(new MemStore()); - await StorageTestSuite.didCreatePrivateKeyTest(new MemStore()); - await StorageTestSuite.keyGenerateTest(new MemStore()); - await StorageTestSuite.keyDeleteTest(new MemStore()); - await StorageTestSuite.keyInsertTest(new MemStore()); - await StorageTestSuite.didListTest(new MemStore()); - await StorageTestSuite.keySignEd25519Test(new MemStore()); - await StorageTestSuite.didPurgeTest(new MemStore()); -} diff --git a/bindings/wasm/examples-account/src/encryption.ts b/bindings/wasm/examples-account/src/encryption.ts deleted file mode 100644 index 3472e9be3b..0000000000 --- a/bindings/wasm/examples-account/src/encryption.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { - AccountBuilder, - AgreementInfo, - CekAlgorithm, - Client, - EncryptionAlgorithm, - MethodContent, - MethodScope, - Resolver, - Storage, -} from "./../../node/identity_wasm.js"; - -/** - * This example demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange - * by encrypting and decrypting data with a shared key. - */ -async function encryption(storage?: Storage) { - let builder = new AccountBuilder({ - storage, - }); - - // Alice creates and publishes their DID Document (see create_did and manipulate_did examples). - let aliceAccount = await builder.createIdentity(); - await aliceAccount.createMethod({ - fragment: "kex-0", - scope: MethodScope.KeyAgreement(), - content: MethodContent.GenerateX25519(), - }); - - // Bob creates and publishes their DID Document (see create_did and manipulate_did examples). - let bobAccount = await builder.createIdentity(); - await bobAccount.createMethod({ - fragment: "kex-0", - scope: MethodScope.KeyAgreement(), - content: MethodContent.GenerateX25519(), - }); - - // 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. - const resolver = new Resolver(); - - // Alice: resolves Bob's DID Document and extracts their public key. - const bobDocument = await resolver.resolve(bobAccount.did()); - const bobMethod = bobDocument.intoDocument().resolveMethod("kex-0", MethodScope.KeyAgreement())!; - const bobPublicKey = bobMethod.data().tryDecode(); - - // Alice encrypts the data using Diffie-Hellman key exchange - const agreementInfo = new AgreementInfo( - Buffer.from("Alice"), - Buffer.from("Bob"), - new Uint8Array(0), - new Uint8Array(0), - ); - const encryptionAlgorithm = EncryptionAlgorithm.A256GCM(); - const cekAlgorithm = CekAlgorithm.EcdhEs(agreementInfo); - const message = Buffer.from("This msg will be encrypted and decrypted"); - const associatedData = Buffer.from("associatedData"); - - const encryptedData = await aliceAccount.encryptData( - message, - associatedData, - encryptionAlgorithm, - cekAlgorithm, - bobPublicKey, - ); - - // Bob must be able to decrypt the message using the shared secret. - const decryptedMessage = await bobAccount.decryptData(encryptedData, encryptionAlgorithm, cekAlgorithm, "kex-0"); - - if (!isArrayEqual(message, decryptedMessage)) throw new Error("decrypted message does not match original message!"); - console.log(`Diffie-Hellman key exchange successful!`); -} - -function isArrayEqual(a: Buffer, b: Uint8Array) { - if (a.length !== b.length) return false; - for (let i = 0; i < a.length; i++) { - if (a[i] !== b[i]) return false; - } - return true; -} - -export { encryption }; diff --git a/bindings/wasm/examples-account/src/lazy.ts b/bindings/wasm/examples-account/src/lazy.ts deleted file mode 100644 index 46a88c238e..0000000000 --- a/bindings/wasm/examples-account/src/lazy.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { AccountBuilder, ExplorerUrl, Storage } from "../../node"; - -/** - * This example demonstrates how to take control over publishing DID updates manually, - * instead of the default automated behavior. - */ -async function lazy(storage?: Storage) { - // Create a new Account with auto publishing set to false. - // This means updates are not pushed to the tangle automatically. - // Rather, when we publish, multiple updates are batched together. - let builder = new AccountBuilder({ - autopublish: false, - storage, - }); - let account = await builder.createIdentity(); - - // Add a new service to the local DID document. - await account.createService({ - fragment: "example-service", - type: "LinkedDomains", - endpoint: "https://example.org", - }); - - // Publish the newly created DID document, - // including the new service, to the tangle. - await account.publish(); - - // Add another service. - await account.createService({ - fragment: "another-service", - type: "LinkedDomains", - endpoint: "https://example.org", - }); - - // Delete the previously added service. - await account.deleteService({ - fragment: "example-service", - }); - - // Publish the updates as one message to the tangle. - await account.publish(); - - // Retrieve the DID of the newly created identity. - let did = account.did(); - - // Print the Explorer URL for the DID. - console.log(`Explorer Url:`, ExplorerUrl.mainnet().resolverUrl(did)); -} - -export { lazy }; diff --git a/bindings/wasm/examples-account/src/manipulate_did.ts b/bindings/wasm/examples-account/src/manipulate_did.ts deleted file mode 100644 index 9cc42eab96..0000000000 --- a/bindings/wasm/examples-account/src/manipulate_did.ts +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { AccountBuilder, ExplorerUrl, MethodContent, MethodRelationship, Storage } from "./../../node/identity_wasm.js"; - -/** - * This example demonstrates how to manipulate a DID Document by adding/removing - * Verification Methods and Services. - */ -async function manipulateIdentity(storage?: Storage) { - // =========================================================================== - // Create Identity - Similar to create_did example - // =========================================================================== - - // Create a new Account with the default configuration. - let builder = new AccountBuilder({ - storage, - }); - let account = await builder.createIdentity(); - - // =========================================================================== - // Identity Manipulation - // =========================================================================== - - // Add another Ed25519 verification method to the identity. - await account.createMethod({ - content: MethodContent.GenerateEd25519(), - fragment: "my-next-key", - }); - - // Associate the newly created method with additional verification relationships. - await account.attachMethodRelationships({ - fragment: "my-next-key", - relationships: [ - MethodRelationship.CapabilityDelegation, - MethodRelationship.CapabilityInvocation, - ], - }); - - // Add a new service to the identity. - await account.createService({ - fragment: "my-service-1", - type: "MyCustomService", - endpoint: "https://example.com", - }); - - // Remove the Ed25519 verification method. - await account.deleteMethod({ fragment: "my-next-key" }); - - // Retrieve the DID of the newly created identity. - let did = account.did(); - - // Print the Explorer URL for the DID. - console.log(`Explorer Url:`, ExplorerUrl.mainnet().resolverUrl(did)); -} - -export { manipulateIdentity }; diff --git a/bindings/wasm/examples-account/src/multiple_identities.ts b/bindings/wasm/examples-account/src/multiple_identities.ts deleted file mode 100644 index 978a0c6104..0000000000 --- a/bindings/wasm/examples-account/src/multiple_identities.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { AccountBuilder, ExplorerUrl, MethodContent, Storage } from "./../../node/identity_wasm.js"; - -/** - * This example demonstrates how to create multiple identities from a builder - * and how to load existing identities into an account. - */ -async function multipleIdentities(storage?: Storage) { - // Create an AccountBuilder to make it easier to create multiple identities. - // Every account created from the builder will use the same storage. - let builder = new AccountBuilder({ - storage, - }); - - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let account1 = await builder.createIdentity(); - - // Create a second identity. - let account2 = await builder.createIdentity(); - - // Retrieve the did of the identity that account1 manages. - let did1 = account1.did(); - - // Suppose we're done with account1 and free it. - account1.free(); - - // Now we want to modify the first identity - how do we do that? - // We can load the identity from storage into an account using the builder. - let account1Reconstructed = await builder.loadIdentity(did1); - - // Now we can make modifications to the identity. - // We can even do so concurrently. - const account1Promise = account1Reconstructed.createMethod({ - content: MethodContent.GenerateEd25519(), - fragment: "my_key", - }); - const account2Promise = account2.createMethod({ - content: MethodContent.GenerateX25519(), - fragment: "my_other_key", - }); - - await Promise.all([account1Promise, account2Promise]); - - // Print the Explorer URL for the DID. - let did = account1Reconstructed.did().toString(); - console.log(`Explorer Url:`, ExplorerUrl.mainnet().resolverUrl(did)); -} - -export { multipleIdentities }; diff --git a/bindings/wasm/examples-account/src/node.ts b/bindings/wasm/examples-account/src/node.ts deleted file mode 100644 index 34c2c10ef0..0000000000 --- a/bindings/wasm/examples-account/src/node.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { config } from "./config"; -import { createIdentity } from "./create_did"; -import { createVC } from "./create_vc"; -import { createVP } from "./create_vp"; -import { storageTestSuite } from "./custom_storage"; -import { encryption } from "./encryption"; -import { lazy } from "./lazy"; -import { manipulateIdentity } from "./manipulate_did"; -import { multipleIdentities } from "./multiple_identities"; -import { revokeVC } from "./revoke_vc"; -import { signing } from "./signing"; -import { unchecked } from "./unchecked"; - -async function main() { - // Check if an example is mentioned - if (process.argv.length != 3) { - throw "Please provide one command line argument with the example name."; - } - - // Take out the argument - let argument = process.argv[2]; - switch (argument) { - case "create_did": - return await createIdentity(); - case "manipulate_did": - return await manipulateIdentity(); - case "lazy": - return await lazy(); - case "signing": - return await signing(); - case "config": - return await config(); - case "unchecked": - return await unchecked(); - case "multiple_identities": - return await multipleIdentities(); - case "encryption": - return await encryption(); - case "create_vc": - return await createVC(); - case "create_vp": - return await createVP(); - case "revoke_vc": - return await revokeVC(); - case "custom_storage": - return await storageTestSuite(); - default: - throw "Unknown example name"; - } -} - -main() - .then((output) => { - console.log("Ok >", output); - }) - .catch((error) => { - console.log("Err >", error); - }); diff --git a/bindings/wasm/examples-account/src/revoke_vc.ts b/bindings/wasm/examples-account/src/revoke_vc.ts deleted file mode 100644 index b3cc563b1f..0000000000 --- a/bindings/wasm/examples-account/src/revoke_vc.ts +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - AccountBuilder, - Credential, - CredentialValidationOptions, - CredentialValidator, - FailFast, - MethodContent, - ProofOptions, - Resolver, - RevocationBitmap, - Storage, -} from "./../../node/identity_wasm.js"; - -/** - This example shows how to revoke a verifiable credential. - It demonstrates two methods for revocation. The first uses a revocation bitmap of type `RevocationBitmap2022`, - while the second method simply removes the verification method (public key) that signed the credential - from the DID Document of the issuer. - - Note that this example uses the "main" network, if you are writing code against the test network then most function - calls will need to include information about the network, since this is not automatically inferred from the - arguments in all cases currently. - **/ -async function revokeVC(storage?: Storage) { - // =========================================================================== - // Create a Verifiable Credential. - // =========================================================================== - - const builder = new AccountBuilder({ - storage, - }); - - // Create an identity for the issuer. - const issuer = await builder.createIdentity(); - - // Add a dedicated verification method to the issuer, with which to sign credentials. - await issuer.createMethod({ - content: MethodContent.GenerateEd25519(), - fragment: "key-1", - }); - - // Add a RevocationBitmap service to the issuer's DID Document. - // This allows verifiers to check whether a credential has been revoked. - const revocationBitmap = new RevocationBitmap(); - await issuer.createService({ - fragment: "my-revocation-service", - type: RevocationBitmap.type(), - endpoint: revocationBitmap.toEndpoint(), - }); - - // Create a credential subject indicating the degree earned by Alice, linked to their DID. - const subject = { - id: "did:iota:B8DucnzULJ9E8cmaReYoePU2b7UKE9WKxyEVov8tQA7H", - name: "Alice", - degree: "Bachelor of Science and Arts", - GPA: "4.0", - }; - - // Create an unsigned `UniversityDegree` credential for Alice. - // The issuer also chooses a unique `RevocationBitmap` index to be able to revoke it later. - const unsignedVc = new Credential({ - id: "https://example.edu/credentials/3732", - type: "UniversityDegreeCredential", - credentialStatus: { - id: issuer.did() + "#my-revocation-service", - type: RevocationBitmap.type(), - revocationBitmapIndex: "5", - }, - issuer: issuer.document().id(), - credentialSubject: subject, - }); - - // Created a signed credential by the issuer. - const signedVc = await issuer.createSignedCredential( - "#key-1", - unsignedVc, - ProofOptions.default(), - ); - - // =========================================================================== - // Revoke the Verifiable Credential. - // =========================================================================== - - // Update the RevocationBitmap service in the issuer's DID Document. - // This revokes the credential's unique index. - await issuer.revokeCredentials("my-revocation-service", 5); - - // Credential verification now fails. - try { - // TODO: uncomment when ported to Stardust. - // CredentialValidator.validate( - // signedVc, - // issuer.document(), - // CredentialValidationOptions.default(), - // FailFast.FirstError - // ); - } catch (e) { - console.log(`Error during validation: ${e}`); - } - - // =========================================================================== - // Alternative revocation of the Verifiable Credential. - // =========================================================================== - - // By removing the verification method, that signed the credential, from the issuer's DID document, - // we effectively revoke the credential, as it will no longer be possible to validate the signature. - await issuer.deleteMethod({ - fragment: "key-1", - }); - - // We expect the verifiable credential to be revoked. - const resolver = new Resolver(); - try { - // Resolve the issuer's updated DID Document to ensure the key was revoked successfully. - const resolvedIssuerDoc = await resolver.resolveCredentialIssuer( - signedVc, - ); - // TODO: uncomment when ported to Stardust. - // CredentialValidator.validate( - // signedVc, - // resolvedIssuerDoc, - // CredentialValidationOptions.default(), - // FailFast.FirstError - // ); - - // `CredentialValidator.validate` will throw an error, hence this will not be reached. - console.log("Revocation failed!"); - } catch (e) { - console.log(`Error during validation: ${e}`); - console.log(`Credential successfully revoked!`); - } -} - -export { revokeVC }; diff --git a/bindings/wasm/examples-account/src/signing.ts b/bindings/wasm/examples-account/src/signing.ts deleted file mode 100644 index f3bd6d3121..0000000000 --- a/bindings/wasm/examples-account/src/signing.ts +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - AccountBuilder, - Credential, - DID, - ExplorerUrl, - KeyPair, - KeyType, - MethodContent, - ProofOptions, - Storage, - VerifierOptions, -} from "./../../node/identity_wasm.js"; - -/** - * This example demonstrates how to issue and sign Verifiable Credentials using the account. - */ -async function signing(storage?: Storage) { - // =========================================================================== - // Create Identity - Similar to create_did example - // =========================================================================== - - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let builder = new AccountBuilder({ - storage, - }); - let account = await builder.createIdentity(); - - // =========================================================================== - // Signing Example - // =========================================================================== - - // Add a new Ed25519 Verification Method to the identity for signing issued verifiable credentials. - await account.createMethod({ - content: MethodContent.GenerateEd25519(), - fragment: "key_1", - }); - - // Prepare a credential subject indicating the degree earned by Alice, linked to their DID. - const subject = { - id: "did:iota:3TT7QsmESw1dcboV2oVTAuJbbxaVYAThexZFct2z5Q2d", - name: "Alice", - degree: { - type: "BachelorDegree", - name: "Bachelor of Science and Arts", - }, - }; - - // Issue an unsigned Credential... - const unsignedVc = new Credential({ - issuer: account.did(), - type: "UniversityDegreeCredential", - credentialSubject: subject, - }); - - // ...and sign the Credential with the previously created Verification Method. - // Note: Different methods are available for different data types, - // use the Method `createSignedData` to sign arbitrary data. - let signedVc = await account.createSignedCredential("key_1", unsignedVc, ProofOptions.default()); - - console.log("[Example] Local Credential", signedVc); - - // Fetch the DID Document from the Tangle. - // - // This is an optional step to ensure DID Document consistency. - let resolved = await account.resolveIdentity(); - - // Retrieve the DID from the newly created identity. - let did = account.did().toString(); - - // Print the Explorer URL for the DID. - console.log(`Explorer Url:`, ExplorerUrl.mainnet().resolverUrl(did)); - - // Ensure the resolved DID Document can verify the credential signature. - let verified = resolved.intoDocument().verifyData(signedVc, VerifierOptions.default()); - - console.log("[Example] Credential Verified = ", verified); -} - -export { signing }; diff --git a/bindings/wasm/examples-account/src/tests/config.ts b/bindings/wasm/examples-account/src/tests/config.ts deleted file mode 100644 index 67243aa1b4..0000000000 --- a/bindings/wasm/examples-account/src/tests/config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { config } from "../config"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Config", async () => { - await config(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/create_did.ts b/bindings/wasm/examples-account/src/tests/create_did.ts deleted file mode 100644 index 01faea13c4..0000000000 --- a/bindings/wasm/examples-account/src/tests/create_did.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createIdentity } from "../create_did"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Create Identity", async () => { - await createIdentity(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/create_vc.ts b/bindings/wasm/examples-account/src/tests/create_vc.ts deleted file mode 100644 index d95afc5611..0000000000 --- a/bindings/wasm/examples-account/src/tests/create_vc.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createVC } from "../create_vc"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Create verifiable credential", async () => { - await createVC(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/create_vp.ts b/bindings/wasm/examples-account/src/tests/create_vp.ts deleted file mode 100644 index 3db9452285..0000000000 --- a/bindings/wasm/examples-account/src/tests/create_vp.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createVP } from "../create_vp"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Create verifiable presentation", async () => { - await createVP(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/encryption.ts b/bindings/wasm/examples-account/src/tests/encryption.ts deleted file mode 100644 index fd940db16f..0000000000 --- a/bindings/wasm/examples-account/src/tests/encryption.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { encryption } from "../encryption"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("encryption", async () => { - await encryption(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/lazy.ts b/bindings/wasm/examples-account/src/tests/lazy.ts deleted file mode 100644 index 9f46c180df..0000000000 --- a/bindings/wasm/examples-account/src/tests/lazy.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { lazy } from "../lazy"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Lazy", async () => { - await lazy(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/manipulate_did.ts b/bindings/wasm/examples-account/src/tests/manipulate_did.ts deleted file mode 100644 index 2b56079d12..0000000000 --- a/bindings/wasm/examples-account/src/tests/manipulate_did.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { manipulateIdentity } from "../manipulate_did"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Manipulate DID", async () => { - await manipulateIdentity(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/multiple_identities.ts b/bindings/wasm/examples-account/src/tests/multiple_identities.ts deleted file mode 100644 index ce835591f1..0000000000 --- a/bindings/wasm/examples-account/src/tests/multiple_identities.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { multipleIdentities } from "../multiple_identities"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Multiple Identities", async () => { - await multipleIdentities(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/revoke_vc.ts b/bindings/wasm/examples-account/src/tests/revoke_vc.ts deleted file mode 100644 index 63e1394849..0000000000 --- a/bindings/wasm/examples-account/src/tests/revoke_vc.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { revokeVC } from "../revoke_vc"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Revoke verifiable credential", async () => { - await revokeVC(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/signing.ts b/bindings/wasm/examples-account/src/tests/signing.ts deleted file mode 100644 index f62e1169be..0000000000 --- a/bindings/wasm/examples-account/src/tests/signing.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { signing } from "../signing"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Signing", async () => { - await signing(); - }); -}); diff --git a/bindings/wasm/examples-account/src/tests/unchecked.ts b/bindings/wasm/examples-account/src/tests/unchecked.ts deleted file mode 100644 index 686e5b9c95..0000000000 --- a/bindings/wasm/examples-account/src/tests/unchecked.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { unchecked } from "../unchecked"; - -// Only verifies that no uncaught exceptions are thrown, including syntax errors etc. -describe("Test node examples", function() { - it("Unchecked", async () => { - await unchecked(); - }); -}); diff --git a/bindings/wasm/examples-account/src/unchecked.ts b/bindings/wasm/examples-account/src/unchecked.ts deleted file mode 100644 index 4ae5010836..0000000000 --- a/bindings/wasm/examples-account/src/unchecked.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { AccountBuilder, ExplorerUrl, Storage, Timestamp } from "./../../node/identity_wasm.js"; - -/** - * This example demonstrates how to update the custom properties of a DID document directly - * and publish it without performing validation. - */ -async function unchecked(storage?: Storage) { - // The creation step generates a keypair, builds an identity - // and publishes it to the IOTA mainnet. - let builder = new AccountBuilder({ - storage, - }); - let account = await builder.createIdentity(); - - // Get a copy of the document this account manages. - // We will apply updates to the document, and overwrite the account's current document. - let document = account.document(); - - // Print the local state of the DID Document - console.log(`[Example] Document before update`, document); - - // Add a custom property to the document. - document.setPropertyUnchecked("myCustomPropertyKey", "value"); - - // Override the updated field timestamp to 01.01.1900 00:00:00. - // because we can. This is usually set automatically when updating via the `Account`. - document.setMetadataUpdated(Timestamp.parse("1900-01-01T00:00:00Z")); - - // Update the identity without validation and publish the result to the Tangle - // (depending on the account's autopublish setting). - // The responsibility is on the caller to provide a valid document which the account - // can continue to use. Failing to do so can corrupt the identity; use with caution! - await account.updateDocumentUnchecked(document); - - // Print the local state of the DID Document after the update. - console.log(`[Example] Document after update`, account.document()); - - // Print the Explorer URL for the DID. - console.log(`Explorer Url:`, ExplorerUrl.mainnet().resolverUrl(account.did())); -} - -export { unchecked }; diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 9935cac0d1..39a2d0509b 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -24,7 +24,6 @@ "cypress": "^10.6.0", "cypress-parallel": "^0.9.1", "fs-extra": "^10.1.0", - "http-server": "^14.1.0", "jsdoc-to-markdown": "^7.1.1", "mocha": "^9.2.0", "ts-mocha": "^9.0.2", @@ -1159,24 +1158,6 @@ } ] }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/basic-auth/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -1850,15 +1831,6 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, - "node_modules/corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2676,12 +2648,6 @@ "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -3206,59 +3172,6 @@ "he": "bin/he" } }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-server": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", - "dev": true, - "dependencies": { - "basic-auth": "^2.0.1", - "chalk": "^4.1.2", - "corser": "^2.0.1", - "he": "^1.2.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy": "^1.18.1", - "mime": "^1.6.0", - "minimist": "^1.2.6", - "opener": "^1.5.1", - "portfinder": "^1.0.28", - "secure-compare": "3.0.1", - "union": "~0.5.0", - "url-join": "^4.0.1" - }, - "bin": { - "http-server": "bin/http-server" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/http-signature": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", @@ -3282,18 +3195,6 @@ "node": ">=8.12.0" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -4618,18 +4519,6 @@ "node": ">=8.6" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -5002,15 +4891,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true, - "bin": { - "opener": "bin/opener-bin.js" - } - }, "node_modules/ospath": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", @@ -5147,50 +5027,6 @@ "node": ">=0.10.0" } }, - "node_modules/portfinder": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", - "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", - "dev": true, - "dependencies": { - "async": "^2.6.4", - "debug": "^3.2.7", - "mkdirp": "^0.5.6" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/portfinder/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -5427,12 +5263,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "node_modules/requizzle": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", @@ -5574,12 +5404,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "dev": true - }, "node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -6406,18 +6230,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dev": true, - "dependencies": { - "qs": "^6.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/unist-util-stringify-position": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", @@ -6485,12 +6297,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -6681,18 +6487,6 @@ "node": ">=10.13.0" } }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -7897,23 +7691,6 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -8405,12 +8182,6 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, - "corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", - "dev": true - }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -9038,12 +8809,6 @@ "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -9419,47 +9184,6 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "requires": { - "whatwg-encoding": "^2.0.0" - } - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-server": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", - "dev": true, - "requires": { - "basic-auth": "^2.0.1", - "chalk": "^4.1.2", - "corser": "^2.0.1", - "he": "^1.2.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy": "^1.18.1", - "mime": "^1.6.0", - "minimist": "^1.2.6", - "opener": "^1.5.1", - "portfinder": "^1.0.28", - "secure-compare": "3.0.1", - "union": "~0.5.0", - "url-join": "^4.0.1" - } - }, "http-signature": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", @@ -9477,15 +9201,6 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -10355,12 +10070,6 @@ "picomatch": "^2.3.1" } }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -10637,12 +10346,6 @@ "mimic-fn": "^2.1.0" } }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true - }, "ospath": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", @@ -10737,46 +10440,6 @@ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, - "portfinder": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", - "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", - "dev": true, - "requires": { - "async": "^2.6.4", - "debug": "^3.2.7", - "mkdirp": "^0.5.6" - }, - "dependencies": { - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - } - } - }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -10954,12 +10617,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "requizzle": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", @@ -11050,12 +10707,6 @@ "ajv-keywords": "^3.5.2" } }, - "secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "dev": true - }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -11674,15 +11325,6 @@ "vfile": "^5.0.0" } }, - "union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dev": true, - "requires": { - "qs": "^6.4.0" - } - }, "unist-util-stringify-position": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz", @@ -11724,12 +11366,6 @@ "punycode": "^2.1.0" } }, - "url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -11868,15 +11504,6 @@ "dev": true, "peer": true }, - "whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "requires": { - "iconv-lite": "0.6.3" - } - }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index e4fc9471d3..71efd4fed8 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -17,12 +17,9 @@ "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/identity_wasm_bg.wasm -o web/identity_wasm_bg.wasm", "build:docs": "node ./build/docs", "build": "npm run build:web && npm run build:nodejs && npm run build:docs", - "example:browser": "http-server ./examples/dist/ -c-1 -o ", - "example:account": "ts-node ./examples-account/src/node.ts", "example:node": "ts-node --project tsconfig.node.json ./examples/src/main.ts", "test": "npm run test:unit && npm run test:unit:node && npm run test:examples", "test:examples": "npm run test:node && npm run test:readme", - "test:account:node": "ts-mocha -p tsconfig.node.json ./examples-account/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", "test:node": "ts-mocha -p tsconfig.node.json ./examples/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", "test:browser:parallel": "cypress-parallel -s test:browser -t 4 -d cypress/e2e -a '\"--quiet\"'", "test:browser": "cypress run --headless", @@ -64,7 +61,6 @@ "cypress": "^10.6.0", "cypress-parallel": "^0.9.1", "fs-extra": "^10.1.0", - "http-server": "^14.1.0", "jsdoc-to-markdown": "^7.1.1", "mocha": "^9.2.0", "ts-mocha": "^9.0.2", diff --git a/bindings/wasm/tests/legacy/account.ts b/bindings/wasm/tests/legacy/account.ts deleted file mode 100644 index a3ba9c5ca4..0000000000 --- a/bindings/wasm/tests/legacy/account.ts +++ /dev/null @@ -1,137 +0,0 @@ -// TODO: Remove or reuse depending on what we do with the account. -// Note that this test is not executed as long as it sits in the legacy directory. -export {}; - -const assert = require("assert"); -const { - AccountBuilder, - AutoSave, - KeyType, - KeyPair, - MethodContent, - MethodScope, - MethodType, -} = require("../node"); - -function setupAccountBuilder() { - return new AccountBuilder({ - autosave: AutoSave.never(), - autopublish: false, - clientConfig: { - nodeSyncDisabled: true, - }, - }); -} - -async function setupAccount() { - return await setupAccountBuilder().createIdentity(); -} - -const privateKeyBytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32]; -const ed25519PublicKeyBytes = [121, 181, 86, 46, 143, 230, 84, 249, 64, 120, 177, 18, 232, 169, 139, 167, 144, 31, 133, - 58, 230, 149, 190, 215, 224, 227, 145, 11, 173, 4, 150, 100]; - -describe("AccountBuilder", function() { - describe("#createIdentity()", function() { - it("should deserialize privateKey Uint8Array correctly", async () => { - const builder = setupAccountBuilder(); - const privateKey = new Uint8Array(privateKeyBytes); - const account = await builder.createIdentity({ - privateKey: privateKey, - }); - assert.equal(account.did().toString(), "did:iota:6Cm9iXWnB4RBrw7ty5u4eBbB5fHzbtjV58VLXWJ2GG8H"); - }); - }); -}); - -describe("Account", function() { - describe("#createMethod()", function() { - it("should deserialize MethodContent privateKey Uint8Array correctly", async () => { - const account = await setupAccount(); - - // Test hard-coded private key. - const fragment1 = "new-key-1"; - await account.createMethod({ - fragment: fragment1, - content: MethodContent.PrivateEd25519(new Uint8Array(privateKeyBytes)), - }); - const method1 = account.document().resolveMethod(fragment1, MethodScope.VerificationMethod()); - assert.equal(method1.id().fragment(), fragment1); - assert.equal(method1.type().toString(), MethodType.Ed25519VerificationKey2018().toString()); - assert.equal(method1.data().tryDecode().toString(), ed25519PublicKeyBytes.toString()); - - // Test KeyPair. - const keypair = new KeyPair(KeyType.X25519); - const fragment2 = "new-key-2"; - await account.createMethod({ - fragment: fragment2, - scope: MethodScope.KeyAgreement(), - content: MethodContent.PrivateX25519(keypair.private()), - }); - const method2 = account.document().resolveMethod(fragment2, MethodScope.KeyAgreement()); - assert.equal(method2.id().fragment(), fragment2); - assert.equal(method2.type().toString(), MethodType.X25519KeyAgreementKey2019().toString()); - assert.equal(method2.data().tryDecode().toString(), keypair.public().toString()); - }); - it("should deserialize MethodContent publicKey Uint8Array correctly", async () => { - const account = await setupAccount(); - - // Test hard-coded public key. - const fragment1 = "new-key-1"; - await account.createMethod({ - fragment: fragment1, - content: MethodContent.PublicEd25519(new Uint8Array(ed25519PublicKeyBytes)), - }); - const method1 = account.document().resolveMethod(fragment1, MethodScope.VerificationMethod()); - assert.equal(method1.id().fragment(), fragment1); - assert.equal(method1.type().toString(), MethodType.Ed25519VerificationKey2018().toString()); - assert.equal(method1.data().tryDecode().toString(), ed25519PublicKeyBytes.toString()); - - // Test KeyPair. - const keypair = new KeyPair(KeyType.X25519); - const fragment2 = "new-key-2"; - await account.createMethod({ - fragment: fragment2, - scope: MethodScope.KeyAgreement(), - content: MethodContent.PublicX25519(keypair.public()), - }); - const method2 = account.document().resolveMethod(fragment2, MethodScope.KeyAgreement()); - assert.equal(method2.id().fragment(), fragment2); - assert.equal(method2.type().toString(), MethodType.X25519KeyAgreementKey2019().toString()); - assert.equal(method2.data().tryDecode().toString(), keypair.public().toString()); - }); - }); - describe("#createService()", function() { - it("should take one type and endpoint", async () => { - const account = await setupAccount(); - - // Test single type & endpoint. - const fragment1 = "new-service-1"; - await account.createService({ - fragment: fragment1, - type: "LinkedDomains", - endpoint: "https://example.com/", - }); - const service = account.document().resolveService(fragment1); - assert.deepStrictEqual(service.id().fragment(), fragment1); - assert.deepStrictEqual(service.type(), ["LinkedDomains"]); - assert.deepStrictEqual(service.serviceEndpoint(), "https://example.com/"); - }); - it("should take multiple types and endpoints", async () => { - const account = await setupAccount(); - - // Test multiple types & endpoints. - const fragment1 = "new-service-1"; - await account.createService({ - fragment: fragment1, - type: ["LinkedDomains", "ExampleType"], - endpoint: ["https://example.com/", "https://iota.org/"], - }); - const service = account.document().resolveService(fragment1); - assert.deepStrictEqual(service.id().fragment(), fragment1); - assert.deepStrictEqual(service.type(), ["LinkedDomains", "ExampleType"]); - assert.deepStrictEqual(service.serviceEndpoint(), ["https://example.com/", "https://iota.org/"]); - }); - }); -}); From 538feb7c011c2e31641df68c094ed105bbb1c387 Mon Sep 17 00:00:00 2001 From: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Mon, 21 Nov 2022 19:33:06 +0100 Subject: [PATCH 4/6] Use alias instead of relative paths in examples (#1081) --- .../wasm/examples/src/0_basic/0_create_did.ts | 4 +- .../wasm/examples/src/0_basic/1_update_did.ts | 2 +- .../examples/src/0_basic/2_resolve_did.ts | 2 +- .../examples/src/0_basic/3_deactivate_did.ts | 2 +- .../wasm/examples/src/0_basic/4_delete_did.ts | 2 +- .../wasm/examples/src/0_basic/5_create_vc.ts | 2 +- .../wasm/examples/src/0_basic/6_create_vp.ts | 2 +- .../wasm/examples/src/0_basic/7_revoke_vc.ts | 2 +- .../src/1_advanced/0_did_controls_did.ts | 2 +- .../src/1_advanced/1_did_issues_nft.ts | 2 +- .../examples/src/1_advanced/2_nft_owns_did.ts | 2 +- .../src/1_advanced/3_did_issues_tokens.ts | 2 +- .../examples/src/1_advanced/4_key_exchange.ts | 2 +- .../src/1_advanced/5_custom_resolution.ts | 2 +- bindings/wasm/package-lock.json | 100 +++++++++++------- bindings/wasm/package.json | 5 +- bindings/wasm/tsconfig.json | 6 +- .../tutorials/validate_university_degree.mdx | 3 + 18 files changed, 87 insertions(+), 57 deletions(-) diff --git a/bindings/wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/examples/src/0_basic/0_create_did.ts index fa6913cf0c..18965c93cd 100644 --- a/bindings/wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/examples/src/0_basic/0_create_did.ts @@ -12,8 +12,8 @@ import { KeyPair, KeyType, MethodScope, -} from "../../../node"; -import { API_ENDPOINT, createDid, ensureAddressHasFunds } from "../util"; +} from "@iota/identity-wasm/node"; +import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** 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/src/0_basic/1_update_did.ts b/bindings/wasm/examples/src/0_basic/1_update_did.ts index 17a9f41213..24cd500c19 100644 --- a/bindings/wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/examples/src/0_basic/1_update_did.ts @@ -14,7 +14,7 @@ import { MethodRelationship, MethodScope, Timestamp, -} from "../../../node"; +} from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to update a DID document in an existing Alias Output. */ diff --git a/bindings/wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/examples/src/0_basic/2_resolve_did.ts index 2abf923795..7eb04590b1 100644 --- a/bindings/wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/examples/src/0_basic/2_resolve_did.ts @@ -4,7 +4,7 @@ import { Bip39 } from "@iota/crypto.js"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import type { IAliasOutput } from "@iota/iota.js"; -import { IotaDocument, IotaIdentityClient } from "../../../node"; +import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to resolve an existing DID in an Alias Output. */ diff --git a/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts index eee15b0917..986dd744cf 100644 --- a/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts @@ -4,7 +4,7 @@ import { Bip39 } from "@iota/crypto.js"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js"; -import { IotaDocument, IotaIdentityClient } from "../../../node"; +import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to deactivate a DID in an Alias Output. */ diff --git a/bindings/wasm/examples/src/0_basic/4_delete_did.ts b/bindings/wasm/examples/src/0_basic/4_delete_did.ts index fb64e4d32b..f19996d6c1 100644 --- a/bindings/wasm/examples/src/0_basic/4_delete_did.ts +++ b/bindings/wasm/examples/src/0_basic/4_delete_did.ts @@ -3,7 +3,7 @@ import { Bip39 } from "@iota/crypto.js"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; -import { IotaIdentityClient } from "../../../node"; +import { IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. */ diff --git a/bindings/wasm/examples/src/0_basic/5_create_vc.ts b/bindings/wasm/examples/src/0_basic/5_create_vc.ts index 4cbef40028..ab3f4707ce 100644 --- a/bindings/wasm/examples/src/0_basic/5_create_vc.ts +++ b/bindings/wasm/examples/src/0_basic/5_create_vc.ts @@ -3,7 +3,7 @@ import { Bip39 } from "@iota/crypto.js"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; -import { Credential, CredentialValidationOptions, CredentialValidator, FailFast, ProofOptions } from "../../../node"; +import { Credential, CredentialValidationOptions, CredentialValidator, FailFast, ProofOptions } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** diff --git a/bindings/wasm/examples/src/0_basic/6_create_vp.ts b/bindings/wasm/examples/src/0_basic/6_create_vp.ts index 9ab4aa9d71..b4d839fc20 100644 --- a/bindings/wasm/examples/src/0_basic/6_create_vp.ts +++ b/bindings/wasm/examples/src/0_basic/6_create_vp.ts @@ -16,7 +16,7 @@ import { SubjectHolderRelationship, Timestamp, VerifierOptions, -} from "../../../node"; +} from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** diff --git a/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts b/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts index 2dd6f578ca..e0dddeed72 100644 --- a/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts +++ b/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts @@ -16,7 +16,7 @@ import { ProofOptions, Resolver, RevocationBitmap, -} from "../../../node"; +} from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** diff --git a/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts b/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts index 039079c68f..ade621f7cc 100644 --- a/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts +++ b/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts @@ -23,7 +23,7 @@ import { KeyPair, KeyType, MethodScope, -} from "../../../node"; +} from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can control another identity. diff --git a/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts b/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts index be0a1457a5..995be36a35 100644 --- a/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts +++ b/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts @@ -22,7 +22,7 @@ import { TransactionHelper, } from "@iota/iota.js"; import { Converter } from "@iota/util.js"; -import { IotaDID, IotaDocument, IotaIdentityClient } from "../../../node"; +import { IotaDID, IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can issue and own NFTs, diff --git a/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts b/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts index d4654e5d05..5f1067af0f 100644 --- a/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts +++ b/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts @@ -23,7 +23,7 @@ import { TransactionHelper, } from "@iota/iota.js"; import { Converter } from "@iota/util.js"; -import { IotaDocument, IotaIdentityClient } from "../../../node"; +import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** Demonstrates how an identity can be owned by NFTs, diff --git a/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts b/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts index 86b876dda6..156384af08 100644 --- a/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts +++ b/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts @@ -24,7 +24,7 @@ import { import type { IFoundryOutput } from "@iota/types"; import { HexHelper } from "@iota/util.js"; import bigInt from "big-integer"; -import { IotaDID, IotaDocument, IotaIdentityClient } from "../../../node"; +import { IotaDID, IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can issue and control a Token Foundry and its tokens. diff --git a/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts b/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts index 9daba1ae1e..f9b377b7ac 100644 --- a/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts +++ b/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts @@ -12,7 +12,7 @@ import { KeyType, MethodScope, X25519, -} from "../../../node"; +} from "@iota/identity-wasm/node"; import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. diff --git a/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts b/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts index 254c13f8ab..694509eaf0 100644 --- a/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts +++ b/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts @@ -1,6 +1,6 @@ import { Bip39 } from "@iota/crypto.js"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; -import { CoreDocument, IotaDocument, IotaIdentityClient, Resolver } from "../../../node"; +import { CoreDocument, IotaDocument, IotaIdentityClient, Resolver } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; // Use this external package to avoid implementing the entire did:key method in this example. diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 39a2d0509b..7948159cfc 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -28,6 +28,7 @@ "mocha": "^9.2.0", "ts-mocha": "^9.0.2", "ts-node": "^10.9.1", + "tsconfig-paths": "^4.1.0", "txm": "^8.0.1", "typescript": "^4.7.2", "wasm-opt": "^1.3.0", @@ -5653,7 +5654,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "optional": true, "engines": { "node": ">=4" } @@ -5978,6 +5978,19 @@ "node": ">=0.3.1" } }, + "node_modules/ts-mocha/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/ts-mocha/node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -6012,6 +6025,19 @@ "node": ">=4.2.0" } }, + "node_modules/ts-mocha/node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/ts-mocha/node_modules/yn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", @@ -6074,29 +6100,17 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz", + "integrity": "sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow==", "dev": true, - "optional": true, "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^2.2.1", "minimist": "^1.2.6", "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "optional": true, - "dependencies": { - "minimist": "^1.2.0" }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=6" } }, "node_modules/tslib": { @@ -10903,8 +10917,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "optional": true + "dev": true }, "strip-final-newline": { "version": "2.0.0", @@ -11143,6 +11156,16 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.0" + } + }, "mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -11168,6 +11191,19 @@ "yn": "^2.0.0" } }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "optional": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "yn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", @@ -11206,28 +11242,14 @@ } }, "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz", + "integrity": "sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow==", "dev": true, - "optional": true, "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^2.2.1", "minimist": "^1.2.6", "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "optional": true, - "requires": { - "minimist": "^1.2.0" - } - } } }, "tslib": { diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 71efd4fed8..7f53a2ba28 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -17,10 +17,10 @@ "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/identity_wasm_bg.wasm -o web/identity_wasm_bg.wasm", "build:docs": "node ./build/docs", "build": "npm run build:web && npm run build:nodejs && npm run build:docs", - "example:node": "ts-node --project tsconfig.node.json ./examples/src/main.ts", + "example:node": "ts-node --project tsconfig.node.json -r tsconfig-paths/register ./examples/src/main.ts", "test": "npm run test:unit && npm run test:unit:node && npm run test:examples", "test:examples": "npm run test:node && npm run test:readme", - "test:node": "ts-mocha -p tsconfig.node.json ./examples/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", + "test:node": "ts-mocha -r tsconfig-paths/register -p tsconfig.node.json ./examples/src/tests/*.ts --parallel --jobs 4 --retries 3 --timeout 180000 --exit", "test:browser:parallel": "cypress-parallel -s test:browser -t 4 -d cypress/e2e -a '\"--quiet\"'", "test:browser": "cypress run --headless", "test:readme": "mocha ./tests/txm_readme.js --retries 3 --timeout 180000 --exit", @@ -65,6 +65,7 @@ "mocha": "^9.2.0", "ts-mocha": "^9.0.2", "ts-node": "^10.9.1", + "tsconfig-paths": "^4.1.0", "txm": "^8.0.1", "typescript": "^4.7.2", "wasm-opt": "^1.3.0", diff --git a/bindings/wasm/tsconfig.json b/bindings/wasm/tsconfig.json index f2b168e758..bf43a27a3a 100644 --- a/bindings/wasm/tsconfig.json +++ b/bindings/wasm/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": ".", "lib": ["ES2020", "DOM"], "declaration": true, "sourceMap": true, @@ -8,7 +9,10 @@ "importsNotUsedAsValues": "error", "noImplicitAny": true, "preserveConstEnums": true, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "paths": { + "@iota/identity-wasm/*": ["./*"] + } }, "exclude": ["node_modules"] } diff --git a/documentation/docs/tutorials/validate_university_degree.mdx b/documentation/docs/tutorials/validate_university_degree.mdx index 53dfe1be6e..b7f6bef555 100644 --- a/documentation/docs/tutorials/validate_university_degree.mdx +++ b/documentation/docs/tutorials/validate_university_degree.mdx @@ -14,6 +14,9 @@ keywords: # Digitally Validate a Degree +:::danger Outdated +This turorial uses the legacy method of the identity library. +::: In this tutorial, you will use the WASM binding of the IOTA Identity framework to digitally prove the existence and validity of a university degree. To follow along please clone [this repository](https://github.com/iotaledger/iota-identity-tutorial). From 5088112726332b451f78a8e5f894422042685ca2 Mon Sep 17 00:00:00 2001 From: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Mon, 21 Nov 2022 20:56:31 +0100 Subject: [PATCH 5/6] dprint fmt (#1089) --- .../wasm/examples/src/0_basic/0_create_did.ts | 4 ++-- .../wasm/examples/src/0_basic/1_update_did.ts | 4 ++-- .../examples/src/0_basic/2_resolve_did.ts | 2 +- .../examples/src/0_basic/3_deactivate_did.ts | 2 +- .../wasm/examples/src/0_basic/4_delete_did.ts | 2 +- .../wasm/examples/src/0_basic/5_create_vc.ts | 8 ++++++- .../wasm/examples/src/0_basic/6_create_vp.ts | 2 +- .../wasm/examples/src/0_basic/7_revoke_vc.ts | 4 ++-- .../src/1_advanced/0_did_controls_did.ts | 18 +++++++-------- .../src/1_advanced/1_did_issues_nft.ts | 2 +- .../examples/src/1_advanced/2_nft_owns_did.ts | 2 +- .../src/1_advanced/3_did_issues_tokens.ts | 2 +- .../examples/src/1_advanced/4_key_exchange.ts | 4 ++-- .../src/1_advanced/5_custom_resolution.ts | 2 +- bindings/wasm/package-lock.json | 23 +++++++++++++++++++ bindings/wasm/package.json | 4 +++- 16 files changed, 58 insertions(+), 27 deletions(-) diff --git a/bindings/wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/examples/src/0_basic/0_create_did.ts index 18965c93cd..7c3b6dab57 100644 --- a/bindings/wasm/examples/src/0_basic/0_create_did.ts +++ b/bindings/wasm/examples/src/0_basic/0_create_did.ts @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; -import { Client, MnemonicSecretManager, SecretManager } from "@iota/iota-client-wasm/node"; -import { Bech32Helper, IAliasOutput } from "@iota/iota.js"; import { IotaDID, IotaDocument, @@ -13,6 +11,8 @@ import { KeyType, MethodScope, } from "@iota/identity-wasm/node"; +import { Client, MnemonicSecretManager, SecretManager } from "@iota/iota-client-wasm/node"; +import { Bech32Helper, IAliasOutput } from "@iota/iota.js"; import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** Demonstrate how to create a DID Document and publish it in a new Alias Output. */ diff --git a/bindings/wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/examples/src/0_basic/1_update_did.ts index 24cd500c19..6611bf10c3 100644 --- a/bindings/wasm/examples/src/0_basic/1_update_did.ts +++ b/bindings/wasm/examples/src/0_basic/1_update_did.ts @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; -import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; -import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js"; import { IotaDocument, IotaIdentityClient, @@ -15,6 +13,8 @@ import { MethodScope, Timestamp, } from "@iota/identity-wasm/node"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to update a DID document in an existing Alias Output. */ diff --git a/bindings/wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/examples/src/0_basic/2_resolve_did.ts index 7eb04590b1..e66526d24e 100644 --- a/bindings/wasm/examples/src/0_basic/2_resolve_did.ts +++ b/bindings/wasm/examples/src/0_basic/2_resolve_did.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; +import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import type { IAliasOutput } from "@iota/iota.js"; -import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to resolve an existing DID in an Alias Output. */ diff --git a/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts index 986dd744cf..535514874b 100644 --- a/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts +++ b/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; +import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js"; -import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to deactivate a DID in an Alias Output. */ diff --git a/bindings/wasm/examples/src/0_basic/4_delete_did.ts b/bindings/wasm/examples/src/0_basic/4_delete_did.ts index f19996d6c1..54795b9806 100644 --- a/bindings/wasm/examples/src/0_basic/4_delete_did.ts +++ b/bindings/wasm/examples/src/0_basic/4_delete_did.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; -import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { IotaIdentityClient } from "@iota/identity-wasm/node"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how to delete a DID in an Alias Output, reclaiming the storage deposit. */ diff --git a/bindings/wasm/examples/src/0_basic/5_create_vc.ts b/bindings/wasm/examples/src/0_basic/5_create_vc.ts index ab3f4707ce..50dadb177b 100644 --- a/bindings/wasm/examples/src/0_basic/5_create_vc.ts +++ b/bindings/wasm/examples/src/0_basic/5_create_vc.ts @@ -2,8 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; +import { + Credential, + CredentialValidationOptions, + CredentialValidator, + FailFast, + ProofOptions, +} from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; -import { Credential, CredentialValidationOptions, CredentialValidator, FailFast, ProofOptions } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** diff --git a/bindings/wasm/examples/src/0_basic/6_create_vp.ts b/bindings/wasm/examples/src/0_basic/6_create_vp.ts index b4d839fc20..9a04933aeb 100644 --- a/bindings/wasm/examples/src/0_basic/6_create_vp.ts +++ b/bindings/wasm/examples/src/0_basic/6_create_vp.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; -import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { Credential, CredentialValidationOptions, @@ -17,6 +16,7 @@ import { Timestamp, VerifierOptions, } from "@iota/identity-wasm/node"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** diff --git a/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts b/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts index e0dddeed72..0d9dadf775 100644 --- a/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts +++ b/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; -import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; -import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js"; import { Credential, CredentialValidationOptions, @@ -17,6 +15,8 @@ import { Resolver, RevocationBitmap, } from "@iota/identity-wasm/node"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { IAliasOutput, IRent, TransactionHelper } from "@iota/iota.js"; import { API_ENDPOINT, createDid } from "../util"; /** diff --git a/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts b/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts index ade621f7cc..65c0d1fd6b 100644 --- a/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts +++ b/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts @@ -2,6 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; +import { + IotaDID, + IotaDocument, + IotaIdentityClient, + IotaVerificationMethod, + KeyPair, + KeyType, + MethodScope, +} from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { AddressTypes, @@ -15,15 +24,6 @@ import { TransactionHelper, } from "@iota/iota.js"; import { Converter } from "@iota/util.js"; -import { - IotaDID, - IotaDocument, - IotaIdentityClient, - IotaVerificationMethod, - KeyPair, - KeyType, - MethodScope, -} from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can control another identity. diff --git a/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts b/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts index 995be36a35..be89c2a6c7 100644 --- a/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts +++ b/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; +import { IotaDID, IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { ADDRESS_UNLOCK_CONDITION_TYPE, @@ -22,7 +23,6 @@ import { TransactionHelper, } from "@iota/iota.js"; import { Converter } from "@iota/util.js"; -import { IotaDID, IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can issue and own NFTs, diff --git a/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts b/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts index 5f1067af0f..c6cd352093 100644 --- a/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts +++ b/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; +import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { ADDRESS_UNLOCK_CONDITION_TYPE, @@ -23,7 +24,6 @@ import { TransactionHelper, } from "@iota/iota.js"; import { Converter } from "@iota/util.js"; -import { IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** Demonstrates how an identity can be owned by NFTs, diff --git a/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts b/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts index 156384af08..e313afb3a8 100644 --- a/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts +++ b/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; +import { IotaDID, IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { ADDRESS_UNLOCK_CONDITION_TYPE, @@ -24,7 +25,6 @@ import { import type { IFoundryOutput } from "@iota/types"; import { HexHelper } from "@iota/util.js"; import bigInt from "big-integer"; -import { IotaDID, IotaDocument, IotaIdentityClient } from "@iota/identity-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; /** Demonstrates how an identity can issue and control a Token Foundry and its tokens. diff --git a/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts b/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts index f9b377b7ac..d32baa4b07 100644 --- a/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts +++ b/bindings/wasm/examples/src/1_advanced/4_key_exchange.ts @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { Bip39 } from "@iota/crypto.js"; -import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; -import { AddressTypes, Bech32Helper, IRent, OutputTypes } from "@iota/iota.js"; import { IotaDocument, IotaIdentityClient, @@ -13,6 +11,8 @@ import { MethodScope, X25519, } from "@iota/identity-wasm/node"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; +import { AddressTypes, Bech32Helper, IRent, OutputTypes } from "@iota/iota.js"; import { API_ENDPOINT, ensureAddressHasFunds } from "../util"; /** Demonstrates Elliptic-curve Diffie-Hellman (ECDH) cryptographic key exchange with DID Documents. diff --git a/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts b/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts index 694509eaf0..80e177ddfb 100644 --- a/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts +++ b/bindings/wasm/examples/src/1_advanced/5_custom_resolution.ts @@ -1,6 +1,6 @@ import { Bip39 } from "@iota/crypto.js"; -import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { CoreDocument, IotaDocument, IotaIdentityClient, Resolver } from "@iota/identity-wasm/node"; +import { Client, MnemonicSecretManager } from "@iota/iota-client-wasm/node"; import { API_ENDPOINT, createDid } from "../util"; // Use this external package to avoid implementing the entire did:key method in this example. diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index 7948159cfc..8c97f6b4f1 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -23,6 +23,7 @@ "copy-webpack-plugin": "^7.0.0", "cypress": "^10.6.0", "cypress-parallel": "^0.9.1", + "dprint": "^0.33.0", "fs-extra": "^10.1.0", "jsdoc-to-markdown": "^7.1.1", "mocha": "^9.2.0", @@ -2477,6 +2478,19 @@ "node": ">=12" } }, + "node_modules/dprint": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/dprint/-/dprint-0.33.0.tgz", + "integrity": "sha512-VploASP7wL1HAYe5xWZKRwp8gW5zTdcG3Tb60DASv6QLnGKsl+OS+bY7wsXFrS4UcIbUNujXdsNG5FxBfRJIQg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "yauzl": "=2.10.0" + }, + "bin": { + "dprint": "bin.js" + } + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -8685,6 +8699,15 @@ "walk-back": "^5.1.0" } }, + "dprint": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/dprint/-/dprint-0.33.0.tgz", + "integrity": "sha512-VploASP7wL1HAYe5xWZKRwp8gW5zTdcG3Tb60DASv6QLnGKsl+OS+bY7wsXFrS4UcIbUNujXdsNG5FxBfRJIQg==", + "dev": true, + "requires": { + "yauzl": "=2.10.0" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 7f53a2ba28..6191df097c 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -26,7 +26,8 @@ "test:readme": "mocha ./tests/txm_readme.js --retries 3 --timeout 180000 --exit", "test:unit": "wasm-pack test --release --node", "test:unit:node": "ts-mocha -p tsconfig.node.json ./tests/*.ts --parallel --exit", - "cypress": "cypress open" + "cypress": "cypress open", + "fmt": "dprint fmt" }, "config": { "CYPRESS_VERIFY_TIMEOUT": 100000 @@ -60,6 +61,7 @@ "copy-webpack-plugin": "^7.0.0", "cypress": "^10.6.0", "cypress-parallel": "^0.9.1", + "dprint": "^0.33.0", "fs-extra": "^10.1.0", "jsdoc-to-markdown": "^7.1.1", "mocha": "^9.2.0", From f2bdd3b8669e79052ae1736487f135cded5526e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Tue, 22 Nov 2022 09:07:33 +0100 Subject: [PATCH 6/6] Use rust stable toolchain in CI (#1083) --- .github/workflows/build-and-test.yml | 1 + .github/workflows/coverage.yml | 53 ------------------------- .github/workflows/format.yml | 4 +- .github/workflows/scripts/coverage.sh | 53 ------------------------- .github/workflows/shared-build-wasm.yml | 1 + .github/workflows/test-docs-build.yml | 1 + 6 files changed, 6 insertions(+), 107 deletions(-) delete mode 100644 .github/workflows/coverage.yml delete mode 100755 .github/workflows/scripts/coverage.sh diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3f511aeb39..59415007e6 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -171,6 +171,7 @@ jobs: uses: actions-rs/toolchain@v1 with: toolchain: stable + profile: minimal override: true - name: Build diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 29693d6f06..0000000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Coverage - -on: - push: - branches: - - main - pull_request: - branches: - - main - - epic/* - paths: - - '.github/workflows/coverage.yml' - - '.github/workflows/scripts/coverage.sh' - - '**.rs' - - '**.toml' - -jobs: - coverage: - runs-on: ubuntu-latest - continue-on-error: false - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2022-06-30 - override: true - components: llvm-tools-preview - - # Download a pre-compiled wasm-bindgen binary. - - name: Install wasm-bindgen-cli - uses: jetli/wasm-bindgen-action@24ba6f9fff570246106ac3f80f35185600c3f6c9 - - - name: Install cargo-binutils - uses: actions-rs/install@v0.1 - with: - crate: cargo-binutils - version: 0.3.4 - - - name: Install rustfilt - uses: actions-rs/install@v0.1 - with: - crate: rustfilt - version: 0.2.1 - - - name: Run test coverage - run: bash .github/workflows/scripts/coverage.sh - - - uses: coverallsapp/github-action@v1.1.2 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: coverage/coverage.info diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 26a595d14d..8f6ed8cefc 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -21,11 +21,13 @@ jobs: steps: - uses: actions/checkout@v2 + + # we use nightly to get access to advanced format capabilities - name: Install rustfmt with nightly toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2022-06-30 + toolchain: nightly override: true components: rustfmt diff --git a/.github/workflows/scripts/coverage.sh b/.github/workflows/scripts/coverage.sh deleted file mode 100755 index edba1030d7..0000000000 --- a/.github/workflows/scripts/coverage.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Remove stale coverage report -rm -rf coverage -mkdir coverage - -NIGHTLY="+nightly-2022-06-30" - -# Run tests with profiling instrumentation -echo "Running instrumented unit tests..." -RUSTFLAGS="-Zinstrument-coverage" LLVM_PROFILE_FILE="identity-%m.profraw" cargo $NIGHTLY test --tests --all --all-features - -# Merge all .profraw files into "identity.profdata" -echo "Merging coverage data..." -PROFRAW="" -for file in $(find . -type f -name "*.profraw"); -do - echo "Found $file" - PROFRAW="${PROFRAW} $file" -done - -cargo $NIGHTLY profdata -- merge ${PROFRAW} -o identity.profdata - -# List the test binaries -echo "Locating test binaries..." -BINARIES="" - -for file in \ - $( \ - RUSTFLAGS="-Zinstrument-coverage" \ - cargo $NIGHTLY test --tests --all --all-features --no-run --message-format=json \ - | jq -r "select(.profile.test == true) | .filenames[]" \ - | grep -v dSYM - \ - ); \ -do - echo "Found $file" - BINARIES="${BINARIES} -object $file" -done - -# Generate and export the coverage report to lcov format -echo "Generating lcov file..." -cargo $NIGHTLY cov -- export ${BINARIES} \ - --instr-profile=identity.profdata \ - --ignore-filename-regex="/.cargo|rustc|target|tests|/.rustup" \ - --format=lcov --Xdemangler=rustfilt \ - >> coverage/coverage.info - - -# Ensure intermediate coverage files are deleted -echo "Removing intermediate files..." -find . -name "*.profraw" -type f -delete -find . -name "*.profdata" -type f -delete diff --git a/.github/workflows/shared-build-wasm.yml b/.github/workflows/shared-build-wasm.yml index f4c73527f5..9bbc6d43a0 100644 --- a/.github/workflows/shared-build-wasm.yml +++ b/.github/workflows/shared-build-wasm.yml @@ -51,6 +51,7 @@ jobs: with: toolchain: stable target: wasm32-unknown-unknown + override: true # Download a pre-compiled wasm-bindgen binary. - name: Install wasm-bindgen-cli diff --git a/.github/workflows/test-docs-build.yml b/.github/workflows/test-docs-build.yml index 997727dd1c..6463e7ab78 100644 --- a/.github/workflows/test-docs-build.yml +++ b/.github/workflows/test-docs-build.yml @@ -26,6 +26,7 @@ jobs: yarn install --immutable yarn build + # docs.rs also uses nightly so it makes sense to mirror that - name: Install Rust nightly toolchain uses: actions-rs/toolchain@v1 with: