diff --git a/.dockerignore b/.dockerignore index 115fe4a561..327b28b236 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,3 @@ target/ -bindings/wasm/ +bindings/wasm/identity_wasm bindings/grpc/target/ diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 994aee5170..5c8d15a1fc 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -17,7 +17,7 @@ on: - '**.toml' - 'bindings/**' - '!bindings/**.md' - - 'bindings/wasm/README.md' # the Readme contain txm tests + - 'bindings/wasm/identity_wasm/README.md' # the Readme contain txm tests env: RUST_BACKTRACE: full @@ -172,7 +172,7 @@ jobs: # run examples only on ubuntu for now if: matrix.os == 'ubuntu-24.04' run: | - cd bindings/wasm + cd bindings/wasm/identity_wasm npm ci npm run test:readme:rust @@ -211,20 +211,20 @@ jobs: - name: Install JS dependencies run: npm ci - working-directory: bindings/wasm + working-directory: bindings/wasm/identity_wasm - - name: Download bindings/wasm artifacts + - name: Download bindings/wasm/identity_wasm artifacts uses: actions/download-artifact@v4 with: name: identity-wasm-bindings-build - path: bindings/wasm + path: bindings/wasm/identity_wasm - name: Start iota sandbox uses: './.github/actions/iota-rebase-sandbox/setup' - name: Run Wasm examples run: npm run test:readme && npm run test:node - working-directory: bindings/wasm + working-directory: bindings/wasm/identity_wasm test-wasm-firefox: needs: build-wasm @@ -247,13 +247,13 @@ jobs: - name: Install JS dependencies run: npm ci - working-directory: bindings/wasm + working-directory: bindings/wasm/identity_wasm - - name: Download bindings/wasm artifacts + - name: Download bindings/wasm/identity_wasm artifacts uses: actions/download-artifact@v4 with: name: identity-wasm-bindings-build - path: bindings/wasm + path: bindings/wasm/identity_wasm - name: Start iota sandbox uses: './.github/actions/iota-rebase-sandbox/setup' @@ -261,8 +261,8 @@ jobs: - name: Build Docker image uses: docker/build-push-action@v6.2.0 with: - context: bindings/wasm/ - file: bindings/wasm/cypress/Dockerfile + context: bindings/wasm/identity_wasm/ + file: bindings/wasm/identity_wasm/cypress/Dockerfile push: false tags: cypress-test:latest load: true @@ -291,13 +291,13 @@ jobs: - name: Install JS dependencies run: npm ci - working-directory: bindings/wasm + working-directory: bindings/wasm/identity_wasm - - name: Download bindings/wasm artifacts + - name: Download bindings/wasm/identity_wasm artifacts uses: actions/download-artifact@v4 with: name: identity-wasm-bindings-build - path: bindings/wasm + path: bindings/wasm/identity_wasm - name: Start iota sandbox uses: './.github/actions/iota-rebase-sandbox/setup' @@ -305,8 +305,8 @@ jobs: - name: Build Docker image uses: docker/build-push-action@v6.2.0 with: - context: bindings/wasm/ - file: bindings/wasm/cypress/Dockerfile + context: bindings/wasm/identity_wasm/ + file: bindings/wasm/identity_wasm/cypress/Dockerfile push: false tags: cypress-test:latest load: true diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index fae65dfde9..72a2376c15 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -40,8 +40,8 @@ jobs: with: args: --all-targets --all-features -- -D warnings - - name: Wasm clippy check + - name: Wasm clippy check identity_wasm uses: actions-rs-plus/clippy-check@b09a9c37c9df7db8b1a5d52e8fe8e0b6e3d574c4 if: ${{ false }} with: - args: --manifest-path ./bindings/wasm/Cargo.toml --target wasm32-unknown-unknown --all-targets --all-features -- -D warnings \ No newline at end of file + args: --manifest-path ./bindings/wasm/identity_wasm/Cargo.toml --target wasm32-unknown-unknown --all-targets --all-features -- -D warnings \ No newline at end of file diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 3e286bdf13..ec09e1bde8 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -45,8 +45,8 @@ jobs: - name: core fmt check run: cargo +nightly fmt --all -- --check - - name: wasm fmt check - run: cargo +nightly fmt --manifest-path ./bindings/wasm/Cargo.toml --all -- --check + - name: wasm fmt check identity_wasm + run: cargo +nightly fmt --manifest-path ./bindings/wasm/identity_wasm/Cargo.toml --all -- --check - name: Cargo.toml fmt check run: diff --git a/.github/workflows/shared-build-wasm.yml b/.github/workflows/shared-build-wasm.yml index 263e1da66c..25be1b761a 100644 --- a/.github/workflows/shared-build-wasm.yml +++ b/.github/workflows/shared-build-wasm.yml @@ -23,7 +23,7 @@ jobs: build-wasm: defaults: run: - working-directory: bindings/wasm + working-directory: bindings/wasm/identity_wasm shell: bash runs-on: ubuntu-latest strategy: @@ -50,7 +50,7 @@ jobs: target-cache-enabled: true sccache-enabled: true sccache-path: ${{ matrix.sccache-path }} - target-cache-path: bindings/wasm/target + target-cache-path: bindings/wasm/identity_wasm/target # Download a pre-compiled wasm-bindgen binary. - name: Install wasm-bindgen-cli @@ -86,9 +86,9 @@ jobs: with: name: ${{ inputs.output-artifact-name }} path: | - bindings/wasm/node - bindings/wasm/web - bindings/wasm/examples/dist - bindings/wasm/docs + bindings/wasm/identity_wasm/node + bindings/wasm/identity_wasm/web + bindings/wasm/identity_wasm/examples/dist + bindings/wasm/identity_wasm/docs if-no-files-found: error retention-days: 1 diff --git a/.github/workflows/wasm-automatic-release-and-publish.yml b/.github/workflows/wasm-automatic-release-and-publish.yml index 1c7b4f141a..873e8b4fa3 100644 --- a/.github/workflows/wasm-automatic-release-and-publish.yml +++ b/.github/workflows/wasm-automatic-release-and-publish.yml @@ -14,7 +14,7 @@ jobs: # owner/repository of workflow has to be static, see https://github.community/t/env-variables-in-uses/17466 uses: iotaledger/identity.rs/.github/workflows/shared-release.yml@main with: - changelog-config-path: ./bindings/wasm/.github_changelog_generator + changelog-config-path: ./bindings/wasm/identity_wasm/.github_changelog_generator pre-release-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+-(?\w+)\.\d+$ main-release-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+$ create-github-release: false diff --git a/.github/workflows/wasm-create-hotfix-pr.yml b/.github/workflows/wasm-create-hotfix-pr.yml index 12f12eb59a..4d3f03ebcc 100644 --- a/.github/workflows/wasm-create-hotfix-pr.yml +++ b/.github/workflows/wasm-create-hotfix-pr.yml @@ -16,8 +16,8 @@ jobs: branch-regex: ^support\/wasm-v[0-9]+\.[0-9]+$ tag-prefix: wasm-v main-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+$ - changelog-config-path: ./bindings/wasm/.github_changelog_generator - changelog-path: ./bindings/wasm/CHANGELOG.md + changelog-config-path: ./bindings/wasm/identity_wasm/.github_changelog_generator + changelog-path: ./bindings/wasm/identity_wasm/CHANGELOG.md release-target: wasm secrets: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} diff --git a/.github/workflows/wasm-create-release-pr.yml b/.github/workflows/wasm-create-release-pr.yml index d5e7df3444..e6ea8c6619 100644 --- a/.github/workflows/wasm-create-release-pr.yml +++ b/.github/workflows/wasm-create-release-pr.yml @@ -27,8 +27,8 @@ jobs: tag-postfix: -${{ github.event.inputs.release-type }}. tag-base: ${{ github.event.inputs.version }} main-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+$ - changelog-config-path: ./bindings/wasm/.github_changelog_generator - changelog-path: ./bindings/wasm/CHANGELOG.md + changelog-config-path: ./bindings/wasm/identity_wasm/.github_changelog_generator + changelog-path: ./bindings/wasm/identity_wasm/CHANGELOG.md pr-body-text: On merge a pre-release will be published to npm. release-target: wasm secrets: @@ -43,8 +43,8 @@ jobs: tag-prefix: wasm-v tag-base: ${{ github.event.inputs.version }} main-tag-regex: ^wasm-v[0-9]+\.[0-9]+\.[0-9]+$ - changelog-config-path: ./bindings/wasm/.github_changelog_generator - changelog-path: ./bindings/wasm/CHANGELOG.md + changelog-config-path: ./bindings/wasm/identity_wasm/.github_changelog_generator + changelog-path: ./bindings/wasm/identity_wasm/CHANGELOG.md release-target: wasm secrets: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} diff --git a/.gitignore b/.gitignore index e039fea6dc..0bcf313dbe 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,7 @@ index.html *.hodl *.hodl.* -!/bindings/wasm/static/index.html +!/bindings/wasm/identity_wasm/static/index.html docs # ignore IOTA build artifacts & package locks diff --git a/.license_template b/.license_template index a437281e00..21571572cb 100644 --- a/.license_template +++ b/.license_template @@ -1,2 +1,2 @@ -// Copyright {20\d{2}(-20\d{2})?} IOTA Stiftung{(?:, .+)?} +{(\/\/ Copyright.*\n)*?}// {(Modifications )?}Copyright {(\(c\) )?}{20\d{2}(-20\d{2})?} IOTA Stiftung{(?:, .+)?} // SPDX-License-Identifier: Apache-2.0 diff --git a/Cargo.toml b/Cargo.toml index bbf08bd310..6f4ea1740b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,9 +23,11 @@ members = [ "identity_ecdsa_verifier", "identity_eddsa_verifier", "examples", + "identity_iota_interaction", + "bindings/wasm/iota_interaction_ts", ] -exclude = ["bindings/wasm", "bindings/grpc"] +exclude = ["bindings/wasm/identity_wasm", "bindings/grpc"] [workspace.dependencies] bls12_381_plus = { version = "0.8.17" } @@ -38,3 +40,11 @@ zkryptium = { version = "0.2.2", default-features = false, features = ["bbsplus" [workspace.lints.clippy] result_large_err = "allow" + +[profile.release.package.iota_interaction_ts] +opt-level = 's' +# Enabling debug for profile.release may lead to more helpfull loged call stacks. +# TODO: Clarify if 'debug = true' facilitates error analysis via console logs. +# If not, remove the next line +# If yes, describe the helping effect in the comment above +# debug = true diff --git a/README.md b/README.md index 630452a28c..6b8e818eac 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentra [Foreign Function Interface (FFI)](https://en.wikipedia.org/wiki/Foreign_function_interface) Bindings of this [Rust](https://www.rust-lang.org/) library to other programming languages: -- [Web Assembly](https://github.com/iotaledger/identity.rs/blob/feat/identity-rebased-alpha/bindings/wasm/) (JavaScript/TypeScript) --> +- [Web Assembly](https://github.com/iotaledger/identity.rs/blob/feat/identity-rebased-alpha/bindings/wasm/identity_wasm/) (JavaScript/TypeScript) --> ## gRPC @@ -99,7 +99,7 @@ _Cargo.toml_ Test this example using https://github.com/anko/txm: `txm README.md` !test program -cd ../.. +cd ../../.. mkdir tmp cat | sed -e 's#identity_iota = { git = "[^"]*", tag = "[^"]*"#identity_iota = { path = "../identity_iota"#' > tmp/Cargo.toml echo '[workspace]' >>tmp/Cargo.toml diff --git a/bindings/grpc/Cargo.toml b/bindings/grpc/Cargo.toml index f2830d183d..4fb877ed88 100644 --- a/bindings/grpc/Cargo.toml +++ b/bindings/grpc/Cargo.toml @@ -20,7 +20,7 @@ path = "src/main.rs" anyhow = "1.0" futures = { version = "0.3" } identity_eddsa_verifier = { path = "../../identity_eddsa_verifier" } -identity_iota = { path = "../../identity_iota", features = ["resolver", "sd-jwt", "domain-linkage", "domain-linkage-fetch", "status-list-2021", "iota-client"] } +identity_iota = { path = "../../identity_iota", features = ["resolver", "sd-jwt", "domain-linkage", "domain-linkage-fetch", "status-list-2021", "iota-client", "send-sync-storage"] } identity_jose = { path = "../../identity_jose" } identity_storage = { path = "../../identity_storage", features = ["memstore"] } identity_stronghold = { path = "../../identity_stronghold", features = ["send-sync-storage"] } diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index b7cee7a287..35cefa4264 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -1,310 +1,105 @@ -# IOTA Identity WASM +# WASM build projects using wasm-bindgen -> This is the 1.0 version of the official WASM bindings for [IOTA Identity](https://github.com/iotaledger/identity.rs). +This folder contains several crates using wasm-bindgen to import or export TS types from & to JS runtimes. These crates +are named _artifact_ in the following to indicate that the NodeJS based JS build system is used instead of cargo. -## [API Reference](https://wiki.iota.org/identity.rs/libraries/wasm/api_reference) +The `build` folder provides build scripts needed to build the artifacts. -## [Examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/examples/README.md) +Here is an overview of the existing artifacts: -## Install the library: +* `identity_wasm`
+ Exports the IdentityClient to Typescript using wasm-bindgen generated wasm bindings -Latest Release: this version matches the `main` branch of this repository. +* `iota_interaction_ts`
+ Imports Typescript IOTA Client SDK types using wasm-bindgen generated wasm bindings + and implements identity_iota_interaction traits (among others, IotaClient and MoveCall traits) for wasm32 platforms. -```bash -npm install @iota/identity-wasm -``` +## Building an Artifact -## Build +For build instructions please have a look into the artifact README file. -Alternatively, you can build the bindings yourself if you have Rust installed. If not, refer to [rustup.rs](https://rustup.rs) for the installation. +## Build process in general -Install [`wasm-bindgen-cli`](https://github.com/rustwasm/wasm-bindgen). A manual installation is required because we use the [Weak References](https://rustwasm.github.io/wasm-bindgen/reference/weak-references.html) feature, which [`wasm-pack` does not expose](https://github.com/rustwasm/wasm-pack/issues/930). +Each artifact is located in its own artifact folder (see above) containing the following important files and subfolders: -```bash -cargo install --force wasm-bindgen-cli -``` +* `tsconfig` files for the `nodejs` and `web` runtimes +* The `package.json` file +* `lib` folder
+ Contains TS files used for wasm-bindings + * Contains `tsconfig` files for the `nodejs` and `web` runtimes with additional TS compiler configurations +* `node` folder
+ Distribution folder for the `nodejs` runtime +* `web` folder
+ Distribution folder for the `web` runtime +* `src` folder
+ Rust code of the crate/artifact +* `tests` folder
+ Test code +* `examples` folder
+ Example code -Then, install the necessary dependencies using: +The build process is defined by run scripts contained in the artifacts `package.json` file . +The build process for the `nodejs` and `web` runtimes, consists of the following steps: -```bash -npm install -``` +* cargo build of the crate with target wasm32-unknown-unknown +* wasm-bindgen CLI call, generating `___.js` and `___.d.ts` files in the distribution folder of the artifact (`node` or + `web`) +* execute the `build/node` or `build/web` build script (see below) +* typescript transpiler call (tsc)
+ Converts the TS files in the `lib` folder into JS files. + JS files are written into the distribution folder of the artifact. + The distribution folder is configured + in the applied tsconfig file (located in the `lib` folder of the artifact). +* execute the `build/replace_paths` build script (see below) -and build the bindings for `node.js` with +## Build scripts contained in the `build` folder -```bash -npm run build:nodejs -``` +### node.js -or for the `web` with +Used by the `bundle:nodejs` run task in the package.json file of the artifact. + +Process steps: + +* Add a [node-fetch polyfill](https://github.com/seanmonstar/reqwest/issues/910) + at the top of the main js file of the artifact +* Generate a `package.json` file derived from the original package.json of the artifact + (done by `utils/generatePackage.js`) + +### web.js + +Used by the `bundle:web` run task in the package.json file of the artifact. + +Process steps: + +* In the main js file of the artifact: + * Comment out a webpack workaround by commenting out all occurrences of
+ `input = new URL(, import.meta.url);` + * Create an init function which imports the artifact wasm file. +* In the typescript source map file `.d.ts`: + * Adds the declaration of the above created init function to the typescript source map file +* Generate a `package.json` file derived from the original package.json file of the artifact + (done by `utils/generatePackage.js`) + +### replace_paths.js + +Processes all JS and TS files contained in the artifact distribution folder that have previously been created +by wasm-bindgen and the TS compiler (tsc) call. + +For each file, it replaces aliases defined in the +[compilerOptions.paths](https://www.typescriptlang.org/docs/handbook/modules/reference.html#paths) +configuration of a specific +tsconfig file by the last entry of the aliases path list (only 1 or 2 paths supported). + +It is used by the following run tasks for the following tsconfig files and distribution folders: + +| run task | tsconfig file | distribution folder | +|----------------------|--------------------------------|---------------------| +| `bundle:nodejs` | `./lib/tsconfig.json` | `node` | +| `bundle:web` | `./lib/tsconfig.web.json` | `web` | +| `build:examples:web` | `./examples/tsconfig.web.json` | `./examples/dist` | -```bash -npm run build:web -``` -## Minimum Requirements -The minimum supported version for node is: `v16` - -## NodeJS Usage - -The following code creates a new IOTA DID Document suitable for publishing to a locally running private network. -See the [instructions](https://github.com/iotaledger/hornet/tree/develop/private_tangle) on running your own private network. - - - - -```typescript -const { - Jwk, - JwkType, - EdCurve, - MethodScope, - IotaDocument, - VerificationMethod, - Service, - MethodRelationship, - IotaIdentityClient, -} = require('@iota/identity-wasm/node'); -const { Client } = require('@iota/sdk-wasm/node'); -const EXAMPLE_JWK = new Jwk({ - kty: JwkType.Okp, - crv: EdCurve.Ed25519, - x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", -}); -// The endpoint of the IOTA node to use. -const API_ENDPOINT = "http://localhost"; - -/** Demonstrate how to create a DID Document. */ -async function main() { - // Create a new client with the given network endpoint. - const client = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - - const didClient = new IotaIdentityClient(client); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkHrp = await didClient.getNetworkHrp(); - - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new IotaDocument(networkHrp); - - // Insert a new Ed25519 verification method in the DID document. - const method = VerificationMethod.newFromJwk( - document.id(), - EXAMPLE_JWK, - "#key-1" - ); - document.insertMethod(method, MethodScope.VerificationMethod()); - - // Attach a new method relationship to the existing method. - document.attachMethodRelationship( - document.id().join("#key-1"), - MethodRelationship.Authentication - ); - - // Add a new Service. - const service = new Service({ - id: document.id().join("#linked-domain"), - type: "LinkedDomains", - serviceEndpoint: "https://iota.org/", - }); - document.insertService(service); - - console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); -} - -main(); -``` - -which prints - -``` -Created document { - "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000", - "verificationMethod": [ - { - "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#key-1", - "controller": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000", - "type": "JsonWebKey", - "publicKeyJwk": { - "kty": "OKP", - "crv": "Ed25519", - "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" - } - } - ], - "authentication": [ - "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#key-1" - ], - "service": [ - { - "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#linked-domain", - "type": "LinkedDomains", - "serviceEndpoint": "https://iota.org/" - } - ] -} -``` - -**NOTE: see the [examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/examples/README.md) for how to publish an IOTA DID Document.** - -## Web Setup - -The library loads the WASM file with an HTTP GET request, so the .wasm file must be copied to the root of the dist folder. - -### Rollup - -- Install `rollup-plugin-copy`: - -```bash -$ npm install rollup-plugin-copy --save-dev -``` - -- Add the copy plugin usage to the `plugins` array under `rollup.config.js`: - -```js -// Include the copy plugin -import copy from "rollup-plugin-copy"; - -// Add the copy plugin to the `plugins` array of your rollup config: -copy({ - targets: [ - { - src: "node_modules/@iota/sdk-wasm/web/wasm/iota_sdk_wasm_bg.wasm", - dest: "public", - rename: "iota_sdk_wasm_bg.wasm", - }, - { - src: "node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm", - dest: "public", - rename: "identity_wasm_bg.wasm", - }, - ], -}); -``` - -### Webpack - -- Install `copy-webpack-plugin`: - -```bash -$ npm install copy-webpack-plugin --save-dev -``` - -```js -// Include the copy plugin -const CopyWebPlugin= require('copy-webpack-plugin'); - -// Add the copy plugin to the `plugins` array of your webpack config: - -new CopyWebPlugin({ - patterns: [ - { - from: 'node_modules/@iota/sdk-wasm/web/wasm/iota_sdk_wasm_bg.wasm', - to: 'iota_sdk_wasm_bg.wasm' - }, - { - from: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', - to: 'identity_wasm_bg.wasm' - } - ] -}), -``` - -### Web Usage - -```typescript -import init, { Client } from "@iota/sdk-wasm/web"; -import * as identity from "@iota/identity-wasm/web"; - -// The endpoint of the IOTA node to use. -const API_ENDPOINT = "http://localhost"; - -const EXAMPLE_JWK = new identity.Jwk({ - kty: identity.JwkType.Okp, - crv: identity.EdCurve.Ed25519, - x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", -}); - -/** Demonstrate how to create a DID Document. */ -async function createDocument() { - // Create a new client with the given network endpoint. - const iotaClient = new Client({ - primaryNode: API_ENDPOINT, - localPow: true, - }); - - const didClient = new identity.IotaIdentityClient(iotaClient); - - // Get the Bech32 human-readable part (HRP) of the network. - const networkHrp = await didClient.getNetworkHrp(); - - // Create a new DID document with a placeholder DID. - // The DID will be derived from the Alias Id of the Alias Output after publishing. - const document = new identity.IotaDocument(networkHrp); - - // Insert a new Ed25519 verification method in the DID document. - let method = identity.VerificationMethod.newFromJwk( - document.id(), - EXAMPLE_JWK, - "#key-1" - ); - document.insertMethod(method, identity.MethodScope.VerificationMethod()); - - // Attach a new method relationship to the existing method. - document.attachMethodRelationship( - document.id().join("#key-1"), - identity.MethodRelationship.Authentication - ); - - // Add a new Service. - const service = new identity.Service({ - id: document.id().join("#linked-domain"), - type: "LinkedDomains", - serviceEndpoint: "https://iota.org/", - }); - document.insertService(service); - - console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); -} - -init() - .then(() => identity.init()) - .then(() => { - await createDocument(); - }); - -// or - -(async () => { - await init(); - await identity.init(); - - await createDocument(); -})(); -// Default path is "identity_wasm_bg.wasm", but you can override it like this -await identity.init("./static/identity_wasm_bg.wasm"); -``` - -Calling `identity.init().then()` or `await identity.init()` is required to load the Wasm file from the server if not available, because of that it will only be slow for the first time. - -**NOTE: see the [examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/examples/README.md) for how to publish an IOTA DID Document.** - -## Examples in the Wild - -You may find it useful to see how the WASM bindings are being used in existing applications: - -- [Zebra IOTA Edge SDK](https://github.com/ZebraDevs/Zebra-Iota-Edge-SDK) (mobile apps using Capacitor.js + Svelte) diff --git a/bindings/wasm/build/node.js b/bindings/wasm/build/node.js index 376b3f5be1..9d1e2151a3 100644 --- a/bindings/wasm/build/node.js +++ b/bindings/wasm/build/node.js @@ -1,11 +1,14 @@ const path = require("path"); const fs = require("fs"); -const { lintAll } = require("./lints"); +const {lintAll} = require("./lints"); const generatePackage = require("./utils/generatePackage"); -const RELEASE_FOLDER = path.join(__dirname, "../node/"); -const entryFilePathNode = path.join(RELEASE_FOLDER, "identity_wasm.js"); +const artifact = process.argv[2]; + +const RELEASE_FOLDER = path.join(__dirname, "..", artifact, "node"); +const entryFilePathNode = path.join(RELEASE_FOLDER, `${artifact}.js`); const entryFileNode = fs.readFileSync(entryFilePathNode).toString(); +console.log(`[build/node.js] Processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`,) lintAll(entryFileNode); @@ -26,10 +29,13 @@ fs.writeFileSync( entryFilePathNode, changedFileNode, ); +console.log(`[build/node.js] Added node-fetch polyfill to entryFile '${entryFilePathNode}'. Starting generatePackage().`,) // Generate `package.json`. const newPackage = generatePackage({ main: "index.js", types: "index.d.ts", + artifact, }); fs.writeFileSync(path.join(RELEASE_FOLDER, "package.json"), JSON.stringify(newPackage, null, 2)); +console.log(`[build/node.js] Finished processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`,) diff --git a/bindings/wasm/build/replace_paths.js b/bindings/wasm/build/replace_paths.js index 4b4199ce5a..0ac2c0e403 100644 --- a/bindings/wasm/build/replace_paths.js +++ b/bindings/wasm/build/replace_paths.js @@ -6,19 +6,20 @@ const path = require("path"); * If more than one path is defined. The second path is used. Otherwise the first path. * @param {string} tsconfig - Path to tsconfig that should be processed * @param {string} dist - Folder of files that should be processed + * @param {string} artifact - Name of the artifact folder. Example: "indentity_wasm" * @param {'resolve'=} mode - In "resolve" mode relative paths will be replaced paths relative to the processed file. Note: `basePath` in the tsconfig will not be considered. */ -function replace(tsconfig, dist, mode) { +function replace(tsconfig, dist, artifact, mode) { // Read tsconfig file. - const tsconfigPath = path.join(__dirname, "..", tsconfig); + const tsconfigPath = path.join(__dirname, "..", artifact, tsconfig); console.log(`\n using ${tsconfigPath}`); - let data = JSON.parse(fs.readFileSync(path.join(__dirname, "..", tsconfig), "utf8")); + let data = JSON.parse(fs.readFileSync(tsconfigPath, "utf8")); let a = data.compilerOptions.paths; let keys = Object.keys(a); // Get `.js` and `.ts` file names from directory. - const distPath = path.join(__dirname, `../${dist}`); + const distPath = path.join(__dirname, "..", artifact, dist); console.log(`\n working in ${distPath}`); let files = readdirSync(distPath); files = files.filter((fileName) => fileName.endsWith(".ts") || fileName.endsWith(".js")); @@ -52,4 +53,4 @@ const readdirSync = (p, a = []) => { return a; }; -replace(process.argv[2], process.argv[3], process.argv[4]); +replace(process.argv[2], process.argv[3], process.argv[4], process.argv[5]); diff --git a/bindings/wasm/build/utils/generatePackage.js b/bindings/wasm/build/utils/generatePackage.js index 25109c2b06..261058869d 100644 --- a/bindings/wasm/build/utils/generatePackage.js +++ b/bindings/wasm/build/utils/generatePackage.js @@ -1,6 +1,5 @@ -const rootPackage = require("../../package.json"); - module.exports = (options) => { + const rootPackage = require(`../../${options.artifact}/package.json`); const newPackage = { name: rootPackage.name, description: rootPackage.description, diff --git a/bindings/wasm/build/web.js b/bindings/wasm/build/web.js index 722072d21e..7aad5d09ba 100644 --- a/bindings/wasm/build/web.js +++ b/bindings/wasm/build/web.js @@ -1,12 +1,15 @@ const path = require("path"); const fs = require("fs"); const fse = require("fs-extra"); -const { lintAll } = require("./lints"); +const {lintAll} = require("./lints"); const generatePackage = require("./utils/generatePackage"); -const RELEASE_FOLDER = path.join(__dirname, "../web/"); -const entryFilePath = path.join(RELEASE_FOLDER, "identity_wasm.js"); +const artifact = process.argv[2]; + +const RELEASE_FOLDER = path.join(__dirname, "..", artifact, "web"); +const entryFilePath = path.join(RELEASE_FOLDER, `${artifact}.js`); const entryFile = fs.readFileSync(entryFilePath).toString(); +console.log(`[build/web.js] Processing entryFile '${entryFilePath}' for artifact '${artifact}'`,) lintAll(entryFile); @@ -26,8 +29,9 @@ fs.writeFileSync( entryFilePath, changedFile, ); +console.log(`[build/web.js] Commented out webpack workaround for '${entryFilePath}'.`,) -const entryFilePathTs = path.join(RELEASE_FOLDER, "identity_wasm.d.ts"); +const entryFilePathTs = path.join(RELEASE_FOLDER, `${artifact}.d.ts`); const entryFileTs = fs.readFileSync(entryFilePathTs).toString(); let changedFileTs = entryFileTs.concat( @@ -43,9 +47,13 @@ fs.writeFileSync( entryFilePathTs, changedFileTs, ); +console.log(`[build/web.js] Created init function for '${entryFilePathTs}'. Starting generatePackage().`,) + // Generate `package.json`. const newPackage = generatePackage({ module: "index.js", types: "index.d.ts", + artifact, }); fs.writeFileSync(path.join(RELEASE_FOLDER, "package.json"), JSON.stringify(newPackage, null, 2)); +console.log(`[build/web.js] Finished processing entryFile '${entryFilePathNode}' for artifact '${artifact}'`,) diff --git a/bindings/wasm/examples/src/main.ts b/bindings/wasm/examples/src/main.ts deleted file mode 100644 index 0a074d3fd2..0000000000 --- a/bindings/wasm/examples/src/main.ts +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2020-2022 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { createIdentity } from "./0_basic/0_create_did"; -import { updateIdentity } from "./0_basic/1_update_did"; -import { resolveIdentity } from "./0_basic/2_resolve_did"; -import { deactivateIdentity } from "./0_basic/3_deactivate_did"; -import { deleteIdentity } from "./0_basic/4_delete_did"; -import { createVC } from "./0_basic/5_create_vc"; -import { createVP } from "./0_basic/6_create_vp"; -import { revokeVC } from "./0_basic/7_revoke_vc"; -import { didControlsDid } from "./1_advanced/0_did_controls_did"; -import { didIssuesNft } from "./1_advanced/1_did_issues_nft"; -import { nftOwnsDid } from "./1_advanced/2_nft_owns_did"; -import { didIssuesTokens } from "./1_advanced/3_did_issues_tokens"; -import { customResolution } from "./1_advanced/4_custom_resolution"; -import { domainLinkage } from "./1_advanced/5_domain_linkage"; -import { sdJwt } from "./1_advanced/6_sd_jwt"; -import { statusList2021 } from "./1_advanced/7_status_list_2021"; -import { zkp } from "./1_advanced/8_zkp"; -import { zkp_revocation } from "./1_advanced/9_zkp_revocation"; - -async function main() { - // Extract example name. - if (process.argv.length != 3) { - throw "Please specify an example name, e.g. '0_create_did'"; - } - const argument = process.argv[2].toLowerCase(); - - switch (argument) { - case "0_create_did": - return await createIdentity(); - case "1_update_did": - return await updateIdentity(); - case "2_resolve_did": - return await resolveIdentity(); - case "3_deactivate_did": - return await deactivateIdentity(); - case "4_delete_did": - return await deleteIdentity(); - case "5_create_vc": - return await createVC(); - case "6_create_vp": - return await createVP(); - case "7_revoke_vc": - return await revokeVC(); - case "0_did_controls_did": - return await didControlsDid(); - case "1_did_issues_nft": - return await didIssuesNft(); - case "2_nft_owns_did": - return await nftOwnsDid(); - case "3_did_issues_tokens": - return await didIssuesTokens(); - case "4_custom_resolution": - return await customResolution(); - case "5_domain_linkage": - return await domainLinkage(); - case "6_sd_jwt": - return await sdJwt(); - case "7_status_list_2021": - return await statusList2021(); - case "8_zkp": - return await zkp(); - case "9_zkp_revocation": - return await zkp_revocation(); - default: - throw "Unknown example name: '" + argument + "'"; - } -} - -main() - .catch((error) => { - console.log("Example error:", error); - }); diff --git a/bindings/wasm/examples/tsconfig.web.json b/bindings/wasm/examples/tsconfig.web.json deleted file mode 100644 index 7cc4be3d7d..0000000000 --- a/bindings/wasm/examples/tsconfig.web.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "ES6", - "outDir": "./dist/web", - "baseUrl": "./", - "lib": ["ES6", "dom"], - "esModuleInterop": true, - "moduleResolution": "node", - "paths": { - "@iota/identity-wasm/node": ["../web"], - "@iota/sdk-wasm/node": ["@iota/sdk-wasm/web"] - } - }, - "exclude": ["tests"] -} diff --git a/bindings/wasm/.cargo/config.toml b/bindings/wasm/identity_wasm/.cargo/config.toml similarity index 100% rename from bindings/wasm/.cargo/config.toml rename to bindings/wasm/identity_wasm/.cargo/config.toml diff --git a/bindings/wasm/.dockerignore b/bindings/wasm/identity_wasm/.dockerignore similarity index 100% rename from bindings/wasm/.dockerignore rename to bindings/wasm/identity_wasm/.dockerignore diff --git a/bindings/wasm/.github_changelog_generator b/bindings/wasm/identity_wasm/.github_changelog_generator similarity index 100% rename from bindings/wasm/.github_changelog_generator rename to bindings/wasm/identity_wasm/.github_changelog_generator diff --git a/bindings/wasm/CHANGELOG.md b/bindings/wasm/identity_wasm/CHANGELOG.md similarity index 100% rename from bindings/wasm/CHANGELOG.md rename to bindings/wasm/identity_wasm/CHANGELOG.md diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml similarity index 52% rename from bindings/wasm/Cargo.toml rename to bindings/wasm/identity_wasm/Cargo.toml index 005f79c78e..43fb63e0fe 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -18,24 +18,30 @@ crate-type = ["cdylib", "rlib"] [dependencies] async-trait = { version = "0.1", default-features = false } console_error_panic_hook = { version = "0.1" } -identity_ecdsa_verifier = { path = "../../identity_ecdsa_verifier", default-features = false, features = ["es256", "es256k"] } -identity_eddsa_verifier = { path = "../../identity_eddsa_verifier", default-features = false, features = ["ed25519"] } +fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto" } +identity_ecdsa_verifier = { path = "../../../identity_ecdsa_verifier", default-features = false, features = ["es256", "es256k"] } +identity_eddsa_verifier = { path = "../../../identity_eddsa_verifier", default-features = false, features = ["ed25519"] } +# Remove iota-sdk dependency while working on issue #1445 +iota-sdk = { version = "1.1.5", default-features = false, features = ["serde", "std"] } js-sys = { version = "0.3.61" } json-proof-token = "0.3.4" proc_typescript = { version = "0.1.0", path = "./proc_typescript" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, branch = "main" } serde = { version = "1.0", features = ["derive"] } +serde-wasm-bindgen = "0.6.5" serde_json = { version = "1.0", default-features = false } serde_repr = { version = "0.1", default-features = false } # Want to use the nice API of tokio::sync::RwLock for now even though we can't use threads. -tokio = { version = "1.29", default-features = false, features = ["sync"] } -wasm-bindgen = { version = "0.2.85", features = ["serde-serialize"] } +tokio = { version = "=1.39.2", default-features = false, features = ["sync"] } +tsify = "0.4.5" +wasm-bindgen = { version = "=0.2.93", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } [dependencies.identity_iota] -path = "../../identity_iota" +path = "../../../identity_iota" default-features = false features = [ - "client", + "iota-client", "revocation-bitmap", "resolver", "domain-linkage", @@ -44,14 +50,28 @@ features = [ "jpt-bbs-plus", ] +# dummy-client dependencies +[dependencies.iota_interaction_ts] +path = "../iota_interaction_ts" +optional = true + [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] getrandom = { version = "0.2", default-features = false, features = ["js"] } [profile.release] opt-level = 's' lto = true +# Enabling debug for profile.release may lead to more helpfull loged call stacks. +# TODO: Clarify if 'debug = true' facilitates error analysis via console logs. +# If not, remove the next line +# If yes, describe the helping effect in the comment above +# debug = true [lints.clippy] # can be removed as soon as fix has been added to clippy # see https://github.com/rust-lang/rust-clippy/issues/12377 empty_docs = "allow" + +[features] +default = ["dummy-client"] +dummy-client = ["dep:iota_interaction_ts"] diff --git a/bindings/wasm/LICENSE b/bindings/wasm/identity_wasm/LICENSE similarity index 100% rename from bindings/wasm/LICENSE rename to bindings/wasm/identity_wasm/LICENSE diff --git a/bindings/wasm/identity_wasm/README.md b/bindings/wasm/identity_wasm/README.md new file mode 100644 index 0000000000..9299d29a15 --- /dev/null +++ b/bindings/wasm/identity_wasm/README.md @@ -0,0 +1,320 @@ +# IOTA Identity WASM + +> This is the 1.0 version of the official WASM bindings for [IOTA Identity](https://github.com/iotaledger/identity.rs). + +## [API Reference](https://wiki.iota.org/identity.rs/libraries/wasm/api_reference) + +## [Examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/identity_wasm/examples/README.md) + +## Install the library: + +Latest Release: this version matches the `main` branch of this repository. + +```bash +npm install @iota/identity-wasm +``` + +## Build + +Alternatively, you can build the bindings yourself if you have Rust installed. If not, refer +to [rustup.rs](https://rustup.rs) for the installation. + +Install [`wasm-bindgen-cli`](https://github.com/rustwasm/wasm-bindgen). A manual installation is required because we use +the [Weak References](https://rustwasm.github.io/wasm-bindgen/reference/weak-references.html) feature, which [ +`wasm-pack` does not expose](https://github.com/rustwasm/wasm-pack/issues/930). + +```bash +cargo install --force wasm-bindgen-cli +``` + +Then, install the necessary dependencies using: + +```bash +npm install +``` + +and build the bindings for `node.js` with + +```bash +npm run build:nodejs +``` + +or for the `web` with + +```bash +npm run build:web +``` + +## Minimum Requirements + +The minimum supported version for node is: `v16` + +## NodeJS Usage + +The following code creates a new IOTA DID Document suitable for publishing to a locally running private network. +See the [instructions](https://github.com/iotaledger/hornet/tree/develop/private_tangle) on running your own private +network. + + + + +```typescript +const { + Jwk, + JwkType, + EdCurve, + MethodScope, + IotaDocument, + VerificationMethod, + Service, + MethodRelationship, + IotaIdentityClient, +} = require('@iota/identity-wasm/node'); +const { Client } = require('@iota/sdk-wasm/node'); + +const EXAMPLE_JWK = new Jwk({ + kty: JwkType.Okp, + crv: EdCurve.Ed25519, + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", +}); + +// The endpoint of the IOTA node to use. +const API_ENDPOINT = "http://localhost"; + +/** Demonstrate how to create a DID Document. */ +async function main() { + // Create a new client with the given network endpoint. + const client = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + + const didClient = new IotaIdentityClient(client); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkHrp = await didClient.getNetworkHrp(); + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + const document = new IotaDocument(networkHrp); + + // Insert a new Ed25519 verification method in the DID document. + const method = VerificationMethod.newFromJwk( + document.id(), + EXAMPLE_JWK, + "#key-1" + ); + document.insertMethod(method, MethodScope.VerificationMethod()); + + // Attach a new method relationship to the existing method. + document.attachMethodRelationship( + document.id().join("#key-1"), + MethodRelationship.Authentication + ); + + // Add a new Service. + const service = new Service({ + id: document.id().join("#linked-domain"), + type: "LinkedDomains", + serviceEndpoint: "https://iota.org/", + }); + document.insertService(service); + + console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); +} + +main(); +``` + +which prints + +``` +Created document { + "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000", + "verificationMethod": [ + { + "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#key-1", + "controller": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" + } + } + ], + "authentication": [ + "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#key-1" + ], + "service": [ + { + "id": "did:iota:tst:0x0000000000000000000000000000000000000000000000000000000000000000#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + } + ] +} +``` + +**NOTE: see +the [examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/identity_wasm/examples/README.md) for +how to publish an IOTA DID Document.** + +## Web Setup + +The library loads the WASM file with an HTTP GET request, so the .wasm file must be copied to the root of the dist +folder. + +### Rollup + +- Install `rollup-plugin-copy`: + +```bash +$ npm install rollup-plugin-copy --save-dev +``` + +- Add the copy plugin usage to the `plugins` array under `rollup.config.js`: + +```js +// Include the copy plugin +import copy from "rollup-plugin-copy"; + +// Add the copy plugin to the `plugins` array of your rollup config: +copy({ + targets: [ + { + src: "node_modules/@iota/sdk-wasm/web/wasm/iota_sdk_wasm_bg.wasm", + dest: "public", + rename: "iota_sdk_wasm_bg.wasm", + }, + { + src: "node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm", + dest: "public", + rename: "identity_wasm_bg.wasm", + }, + ], +}); +``` + +### Webpack + +- Install `copy-webpack-plugin`: + +```bash +$ npm install copy-webpack-plugin --save-dev +``` + +```js +// Include the copy plugin +const CopyWebPlugin= require('copy-webpack-plugin'); + +// Add the copy plugin to the `plugins` array of your webpack config: + +new CopyWebPlugin({ + patterns: [ + { + from: 'node_modules/@iota/sdk-wasm/web/wasm/iota_sdk_wasm_bg.wasm', + to: 'iota_sdk_wasm_bg.wasm' + }, + { + from: 'node_modules/@iota/identity-wasm/web/identity_wasm_bg.wasm', + to: 'identity_wasm_bg.wasm' + } + ] +}), +``` + +### Web Usage + +```typescript +import init, { Client } from "@iota/sdk-wasm/web"; +import * as identity from "@iota/identity-wasm/web"; + +// The endpoint of the IOTA node to use. +const API_ENDPOINT = "http://localhost"; + +const EXAMPLE_JWK = new identity.Jwk({ + kty: identity.JwkType.Okp, + crv: identity.EdCurve.Ed25519, + x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", +}); + +/** Demonstrate how to create a DID Document. */ +async function createDocument() { + // Create a new client with the given network endpoint. + const iotaClient = new Client({ + primaryNode: API_ENDPOINT, + localPow: true, + }); + + const didClient = new identity.IotaIdentityClient(iotaClient); + + // Get the Bech32 human-readable part (HRP) of the network. + const networkHrp = await didClient.getNetworkHrp(); + + // Create a new DID document with a placeholder DID. + // The DID will be derived from the Alias Id of the Alias Output after publishing. + const document = new identity.IotaDocument(networkHrp); + + // Insert a new Ed25519 verification method in the DID document. + let method = identity.VerificationMethod.newFromJwk( + document.id(), + EXAMPLE_JWK, + "#key-1" + ); + document.insertMethod(method, identity.MethodScope.VerificationMethod()); + + // Attach a new method relationship to the existing method. + document.attachMethodRelationship( + document.id().join("#key-1"), + identity.MethodRelationship.Authentication + ); + + // Add a new Service. + const service = new identity.Service({ + id: document.id().join("#linked-domain"), + type: "LinkedDomains", + serviceEndpoint: "https://iota.org/", + }); + document.insertService(service); + + console.log(`Created document `, JSON.stringify(document.toJSON(), null, 2)); +} + +init() + .then(() => identity.init()) + .then(() => { + await createDocument(); + }); + +// or + +(async () => { + await init(); + await identity.init(); + + await createDocument(); +})(); + +// Default path is "identity_wasm_bg.wasm", but you can override it like this +await identity.init("./static/identity_wasm_bg.wasm"); +``` + +Calling `identity.init().then()` or `await identity.init()` is required to load the Wasm file from the server +if not available, because of that it will only be slow for the first time. + +**NOTE: see +the [examples](https://github.com/iotaledger/identity.rs/blob/main/bindings/wasm/identity_wasm/examples/README.md) for +how to publish an IOTA DID Document.** + +## Examples in the Wild + +You may find it useful to see how the WASM bindings are being used in existing applications: + +- [Zebra IOTA Edge SDK](https://github.com/ZebraDevs/Zebra-Iota-Edge-SDK) (mobile apps using Capacitor.js + Svelte) diff --git a/bindings/wasm/cypress.config.ts b/bindings/wasm/identity_wasm/cypress.config.ts similarity index 100% rename from bindings/wasm/cypress.config.ts rename to bindings/wasm/identity_wasm/cypress.config.ts diff --git a/bindings/wasm/cypress/Dockerfile b/bindings/wasm/identity_wasm/cypress/Dockerfile similarity index 100% rename from bindings/wasm/cypress/Dockerfile rename to bindings/wasm/identity_wasm/cypress/Dockerfile diff --git a/bindings/wasm/cypress/e2e/0_basic/0_create_did.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/0_basic/0_create_did.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/0_basic/0_create_did.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/0_basic/0_create_did.cy.js diff --git a/bindings/wasm/cypress/e2e/0_basic/1_update_did.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/0_basic/1_update_did.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/0_basic/1_update_did.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/0_basic/1_update_did.cy.js diff --git a/bindings/wasm/cypress/e2e/0_basic/2_resolve_did.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/0_basic/2_resolve_did.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/0_basic/2_resolve_did.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/0_basic/2_resolve_did.cy.js diff --git a/bindings/wasm/cypress/e2e/0_basic/3_deactivate_did.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/0_basic/3_deactivate_did.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/0_basic/3_deactivate_did.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/0_basic/3_deactivate_did.cy.js diff --git a/bindings/wasm/cypress/e2e/0_basic/4_delete_did.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/0_basic/4_delete_did.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/0_basic/4_delete_did.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/0_basic/4_delete_did.cy.js diff --git a/bindings/wasm/cypress/e2e/0_basic/5_create_vc.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/0_basic/5_create_vc.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/0_basic/5_create_vc.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/0_basic/5_create_vc.cy.js diff --git a/bindings/wasm/cypress/e2e/0_basic/6_create_vp.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/0_basic/6_create_vp.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/0_basic/6_create_vp.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/0_basic/6_create_vp.cy.js diff --git a/bindings/wasm/cypress/e2e/0_basic/7_revoke_vc.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/0_basic/7_revoke_vc.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/0_basic/7_revoke_vc.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/0_basic/7_revoke_vc.cy.js diff --git a/bindings/wasm/cypress/e2e/1_advanced/0_did_controls_did.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/1_advanced/0_did_controls_did.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/1_advanced/0_did_controls_did.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/1_advanced/0_did_controls_did.cy.js diff --git a/bindings/wasm/cypress/e2e/1_advanced/1_did_issues_nft.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/1_advanced/1_did_issues_nft.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/1_advanced/1_did_issues_nft.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/1_advanced/1_did_issues_nft.cy.js diff --git a/bindings/wasm/cypress/e2e/1_advanced/2_nft_owns_did.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/1_advanced/2_nft_owns_did.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/1_advanced/2_nft_owns_did.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/1_advanced/2_nft_owns_did.cy.js diff --git a/bindings/wasm/cypress/e2e/1_advanced/3_did_issues_tokens.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/1_advanced/3_did_issues_tokens.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/1_advanced/3_did_issues_tokens.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/1_advanced/3_did_issues_tokens.cy.js diff --git a/bindings/wasm/cypress/e2e/1_advanced/4_custom_resolution.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/1_advanced/4_custom_resolution.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/1_advanced/4_custom_resolution.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/1_advanced/4_custom_resolution.cy.js diff --git a/bindings/wasm/cypress/e2e/1_advanced/5_domain_linkage.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/1_advanced/5_domain_linkage.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/1_advanced/5_domain_linkage.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/1_advanced/5_domain_linkage.cy.js diff --git a/bindings/wasm/cypress/e2e/1_advanced/6_sd_jwt.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/1_advanced/6_sd_jwt.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/1_advanced/6_sd_jwt.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/1_advanced/6_sd_jwt.cy.js diff --git a/bindings/wasm/cypress/e2e/1_advanced/7_status_list_2021.cy.js b/bindings/wasm/identity_wasm/cypress/e2e/1_advanced/7_status_list_2021.cy.js similarity index 100% rename from bindings/wasm/cypress/e2e/1_advanced/7_status_list_2021.cy.js rename to bindings/wasm/identity_wasm/cypress/e2e/1_advanced/7_status_list_2021.cy.js diff --git a/bindings/wasm/cypress/fixtures/.gitkeep b/bindings/wasm/identity_wasm/cypress/fixtures/.gitkeep similarity index 100% rename from bindings/wasm/cypress/fixtures/.gitkeep rename to bindings/wasm/identity_wasm/cypress/fixtures/.gitkeep diff --git a/bindings/wasm/cypress/plugins/.gitkeep b/bindings/wasm/identity_wasm/cypress/plugins/.gitkeep similarity index 100% rename from bindings/wasm/cypress/plugins/.gitkeep rename to bindings/wasm/identity_wasm/cypress/plugins/.gitkeep diff --git a/bindings/wasm/cypress/support/.gitkeep b/bindings/wasm/identity_wasm/cypress/support/.gitkeep similarity index 100% rename from bindings/wasm/cypress/support/.gitkeep rename to bindings/wasm/identity_wasm/cypress/support/.gitkeep diff --git a/bindings/wasm/cypress/support/setup.js b/bindings/wasm/identity_wasm/cypress/support/setup.js similarity index 100% rename from bindings/wasm/cypress/support/setup.js rename to bindings/wasm/identity_wasm/cypress/support/setup.js diff --git a/bindings/wasm/examples/README.md b/bindings/wasm/identity_wasm/examples/README.md similarity index 93% rename from bindings/wasm/examples/README.md rename to bindings/wasm/identity_wasm/examples/README.md index 74914481b9..f467c89c07 100644 --- a/bindings/wasm/examples/README.md +++ b/bindings/wasm/identity_wasm/examples/README.md @@ -37,7 +37,7 @@ npm run example:node -- 0_create_did The following basic CRUD (Create, Read, Update, Delete) examples are available: | Name | Information | -| :-------------------------------------------------- | :----------------------------------------------------------------------------------- | +|:----------------------------------------------------|:-------------------------------------------------------------------------------------| | [0_create_did](src/0_basic/0_create_did.ts) | Demonstrates how to create a DID Document and publish it in a new Alias Output. | | [1_update_did](src/0_basic/1_update_did.ts) | Demonstrates how to update a DID document in an existing Alias Output. | | [2_resolve_did](src/0_basic/2_resolve_did.ts) | Demonstrates how to resolve an existing DID in an Alias Output. | @@ -52,7 +52,7 @@ The following basic CRUD (Create, Read, Update, Delete) examples are available: The following advanced examples are available: | Name | Information | -| :----------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | +|:-------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------| | [0_did_controls_did](src/1_advanced/0_did_controls_did.ts) | Demonstrates how an identity can control another identity. | | [1_did_issues_nft](src/1_advanced/1_did_issues_nft.ts) | Demonstrates how an identity can issue and own NFTs, and how observers can verify the issuer of the NFT. | | [2_nft_owns_did](src/1_advanced/2_nft_owns_did.ts) | Demonstrates how an identity can be owned by NFTs, and how observers can verify that relationship. | diff --git a/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts new file mode 100644 index 0000000000..060e88c604 --- /dev/null +++ b/bindings/wasm/identity_wasm/examples/src/0_basic/-1_test_api_call.ts @@ -0,0 +1,394 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { + ControllerAndVotingPower, + convertToAddress, + IotaDID, + IotaDocument, + Jwk, + JwkMemStore, + JwsAlgorithm, + KeyIdMemStore, + KinesisIdentityClient, + Multicontroller, + ProposalAction, + Storage, + // StorageSigner, +} from "@iota/identity-wasm/node"; + +import { executeTransaction } from "@iota/iota-interaction-ts/lib/iota_client_helpers"; +import { bcs } from "@iota/iota.js/bcs"; +import { IotaClient as KinesisClient } from "@iota/iota.js/client"; +import { getFaucetHost, requestIotaFromFaucetV0, requestIotaFromFaucetV1 } from "@iota/iota.js/faucet"; +import { Ed25519Keypair, Ed25519PublicKey } from "@iota/iota.js/keypairs/ed25519"; +import { Transaction } from "@iota/iota.js/transactions"; +import { IOTA_TYPE_ARG } from "@iota/iota.js/utils"; + +// this was is the implemented in `src/kinesis/wasm_identity_client.rs` +// and then imported here as `convertToAddress` from `@iota/identity-wasm/node` (see above) +// +// use fastcrypto::ed25519::Ed25519PublicKey; +// use identity_iota::iota::iota_sdk_abstraction::types::base_types::IotaAddress; +// use identity_iota::iota::sui_name_tbd_error::Error as TbdError; +// +/// TODO: consider importing function from rebased later on, if possible +// pub fn convert_to_address(sender_public_key: &[u8]) -> Result { +// let public_key = Ed25519PublicKey::from_bytes(sender_public_key) +// .map_err(|err| TbdError::InvalidKey(format!("could not parse public key to Ed25519 public key; {err}")))?; +// +// Ok(IotaAddress::from(&public_key)) +// } +// +// #[wasm_bindgen(js_name = convertToAddress)] +// pub fn wasm_convert_to_address(sender_public_key: &[u8]) -> Result { +// convert_to_address(sender_public_key) +// .map(|v| v.to_string()) +// .map_err(|err| JsError::new(&format!("could not derive address from public key; {err}"))) +// } +// +// Currently unclear where to put the this implementation atm. It was a helper for testing performing transactions, +// we might also be able to just drop it due to API restructuring. + +export const DEFAULT_GAS_BUDGET = 10000000; +const NETWORK_NAME = "local"; +const NETWORK_NAME_FAUCET = "localnet"; +const NETWORK_URL = "http://127.0.0.1:9000"; +const IDENTITY_IOTA_PACKAGE_ID = "0x7e0ccc737a8def97f37fe9f70267a14bc0fe0871c12f8742fac5e3baf58eb45b"; + +async function initializeClients() { + const kinesis_client = new KinesisClient({ url: NETWORK_URL }); + + console.log("---------------- Preparing IdentityClient ------------------------"); + const VALID_SECP256K1_SECRET_KEY = [ + 59, + 148, + 11, + 85, + 134, + 130, + 61, + 253, + 2, + 174, + 59, + 70, + 27, + 180, + 51, + 107, + 94, + 203, + 174, + 253, + 102, + 39, + 170, + 146, + 46, + 252, + 4, + 143, + 236, + 12, + 136, + 28, + ]; + const secret_key = new Uint8Array(VALID_SECP256K1_SECRET_KEY); + let key_pair = Ed25519Keypair.fromSecretKey(secret_key); + let pub_key = key_pair.getPublicKey(); + console.log(`Created Ed25519Keypair with PublicKey ${pub_key.toBase64()} and address ${pub_key.toIotaAddress()}`); + + // test builder and create instance for other tests + let identityClient = KinesisIdentityClient + .builder() + .identityIotaPackageId(IDENTITY_IOTA_PACKAGE_ID) + .senderPublicKey(pub_key.toRawBytes()) + .senderAddress(pub_key.toIotaAddress()) + .iotaClient(kinesis_client) + .networkName(NETWORK_NAME) + .build(); + + await requestIotaFromFaucetV0({ + host: getFaucetHost(NETWORK_NAME_FAUCET), + recipient: identityClient.senderAddress(), + }); + + const balance = await kinesis_client.getBalance({ owner: identityClient.senderAddress() }); + if (balance.totalBalance === "0") { + throw new Error("Balance is still 0"); + } else { + console.log(`Received gas from faucet: ${balance.totalBalance} for owner ${identityClient.senderAddress()}`); + } + + return { kinesis_client, identityClient, key_pair }; +} + +async function testIdentityClient( + identityClient: KinesisIdentityClient, + kinesis_client: KinesisClient, + key_pair: Ed25519Keypair, +): Promise { + console.log("\n-------------- Start testIdentityClient -------------------------------"); + console.log(`chainIdentifier: ${await identityClient.getChainIdentifier()}`); + console.log(`senderPublicKey: ${identityClient.senderPublicKey()}`); + console.log(`senderAddress: ${identityClient.senderAddress()}`); + console.log(`networkName: ${identityClient.networkName()}`); + + try { + console.log("\n---------------- executeDummyTransaction ------------------------"); + let coins = await kinesis_client.getCoins({ + owner: identityClient.senderAddress(), + coinType: IOTA_TYPE_ARG, + }); + const tx = new Transaction(); + const coin_0 = coins.data[0]; + const coin = tx.splitCoins(tx.object(coin_0.coinObjectId), [ + bcs.u64().serialize(DEFAULT_GAS_BUDGET * 2), + ]); + tx.transferObjects([coin], identityClient.senderAddress()); + tx.setSenderIfNotSet(key_pair.getPublicKey().toIotaAddress()); + const signatureWithBytes = await tx.sign({ signer: key_pair, client: kinesis_client }); + + const response = await identityClient.executeDummyTransaction( + signatureWithBytes.bytes, + [signatureWithBytes.signature], + ); + console.dir(response); + + // The above transaction execution is equivalent to the following snippet using the TS SDK iota client + const response2 = await kinesis_client.executeTransactionBlock({ + transactionBlock: signatureWithBytes.bytes, + signature: signatureWithBytes.signature, + }); + console.log(`TX result: ${response2}`); + } catch (ex) { + console.log(`\nTest execute_dummy_transaction() - Error: ${(ex as Error).message}`); + } + + try { + console.log("\n---------------- getIdentity ------------------------"); + await identityClient.getIdentity("foobar"); + } catch (ex) { + console.log(`Test getIdentity() - Error: ${(ex as Error).message}`); + } + + const did4resolveDid = IotaDID.parse("did:iota:0x0101010101010101010101010101010101010101010101010101010101010101"); + try { + // console.log("\n---------------- resolveDid ------------------------"); + // not implemented + // await identityClient.resolveDid(did4resolveDid); + } catch (ex) { + console.log(`Test resolveDid() - Error: ${(ex as Error).message}`); + } + + const document1 = new IotaDocument("foobar"); + try { + // console.log("\n---------------- publishDidDocument ------------------------"); + // not implemented + // await identityClient.publishDidDocument(document1, BigInt(12345), "dummy signer"); + } catch (ex) { + console.log(`Test publishDidDocument() - Error: ${(ex as Error).message}`); + } + + const document2 = new IotaDocument("foobar"); + try { + // not implemented + // console.log("\n---------------- publishDidDocumentUpdate ------------------------"); + // await identityClient.publishDidDocumentUpdate(document2, BigInt(12345), "dummy signer"); + } catch (ex) { + console.log(`Test publishDidDocumentUpdate() - Error: ${(ex as Error).message}`); + } + + const did4deactivateDidOutput = IotaDID.parse( + "did:iota:0x0101010101010101010101010101010101010101010101010101010101010101", + ); + try { + // not implemented + // console.log("\n---------------- deactivateDidOutput ------------------------"); + // await identityClient.deactivateDidOutput(did4deactivateDidOutput, BigInt(12345), "dummy signer"); + } catch (ex) { + console.log(`Test deactivateDidOutput() - Error: ${(ex as Error).message}`); + } +} + +function testMultiController(): void { + let multiController = new Multicontroller(); + + const testCapId = "123"; + console.dir(multiController.controlledValue()); + console.dir(multiController.controllerVotingPower(testCapId)); + console.dir(multiController.hasMember(testCapId)); + console.dir(multiController.intoInner()); + console.dir(multiController.proposals()); + console.dir(multiController.threshold()); +} + +async function testProposals(identityClient: KinesisIdentityClient): Promise { + let action: ProposalAction = "Deactivate"; + console.dir(action); + + action = { UpdateDocument: new IotaDocument("foobar") }; + console.dir(action); + console.dir(action.UpdateDocument); + console.dir(action.UpdateDocument.id()); + console.dir(action.UpdateDocument.toJSON()); + + let identity = await identityClient + .createIdentity(Uint8Array.from([1, 2, 3])) + .threshold(BigInt(1)) + .gasBudget(BigInt(1)) + .controllers([ + new ControllerAndVotingPower("one", BigInt(1)), + new ControllerAndVotingPower("two", BigInt(2)), + ]) + .finish(identityClient, "dummySigner"); + console.dir(identity); + console.dir(identity.isShared()); + console.dir(identity.proposals()); + const deactivateProposal = await identity + .deactivateDid() + .expirationEpoch(BigInt(1)) + .gasBudget(BigInt(1)) + .key("key") + .finish(identityClient, "dummySigner"); + console.dir(deactivateProposal); + + // proposals consume the identity instance, so we need a new one + identity = await identityClient + .createIdentity(Uint8Array.from([1, 2, 3])) + .threshold(BigInt(1)) + .gasBudget(BigInt(1)) + .controllers([ + new ControllerAndVotingPower("one", BigInt(1)), + new ControllerAndVotingPower("two", BigInt(2)), + ]) + .finish(identityClient, "dummySigner"); + + const updateProposal = await identity + .updateDidDocument(new IotaDocument("foobar")) + .expirationEpoch(BigInt(1)) + .gasBudget(BigInt(1)) + .key("key") + .finish(identityClient, "dummySigner"); + console.dir(updateProposal); +} + +// async function signerTest(): Promise { +// // create new storage +// const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); +// +// // generate new key +// let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); +// let publicKeyJwk = generate.jwk().toPublic(); +// if (typeof publicKeyJwk === "undefined") { +// throw new Error("failed to derive public JWK from generated JWK"); +// } +// let keyId = generate.keyId(); +// console.dir({ +// keyId, +// publicKeyJwk: publicKeyJwk, +// }); +// +// // create signer from storage +// let signer = new StorageSigner(storage, keyId, publicKeyJwk); +// console.log({ keyIdFromSigner: signer.keyId() }); +// +// // sign test +// let signed = await signer.sign(new Uint8Array([0, 1, 2, 4])); +// console.dir({ signed }); +// } + +// async function testExecuteTransaction(kinesis_client: KinesisClient) { +// console.log("---------------- testing executeTransaction ------------------------"); +// +// // create new storage +// const storage: Storage = new Storage(new JwkMemStore(), new KeyIdMemStore()); +// +// // generate new key +// let generate = await storage.keyStorage().generate("Ed25519", JwsAlgorithm.EdDSA); +// let publicKeyJwk = generate.jwk().toPublic(); +// if (typeof publicKeyJwk === "undefined") { +// throw new Error("failed to derive public JWK from generated JWK"); +// } +// +// // create signer from storage +// let signer = new StorageSigner(storage, generate.keyId(), publicKeyJwk); +// // get public key as bytes and create address +// let publicJwk = (signer as any).publicKeyRaw(); +// let address = convertToAddress(publicJwk); +// +// await requestIotaFromFaucetV0({ +// host: getFaucetHost(NETWORK_NAME_FAUCET), +// recipient: address, +// }); +// +// // try to craft tx with js api +// let coins = await kinesis_client.getCoins({ +// owner: address, +// coinType: IOTA_TYPE_ARG, +// }); +// const tx = new Transaction(); +// const coin_0 = coins.data[0]; +// const coin = tx.splitCoins(tx.object(coin_0.coinObjectId), [ +// bcs.u64().serialize(DEFAULT_GAS_BUDGET * 2), +// ]); +// tx.transferObjects([coin], address); +// tx.setSenderIfNotSet(address); +// +// let response = await executeTransaction( +// kinesis_client, +// address, +// publicJwk, +// await tx.build({ client: kinesis_client }), +// signer, +// ); +// console.dir(response); +// console.dir(response?.response?.transaction?.data); +// } + +/** Test API usage */ +export async function testApiCall(): Promise { + const { kinesis_client, identityClient, key_pair } = await initializeClients(); + + type ProgrammableTransaction = ReturnType; + let tt: ProgrammableTransaction = bcs.ProgrammableTransaction.parse(new Uint8Array([])); + console.dir(tt); + + try { + await testIdentityClient(identityClient, kinesis_client, key_pair); + } catch (err) { + const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + console.error(`identity client binding test failed: ${suffix}`); + } + + try { + testMultiController(); + } catch (err) { + const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + console.error(`multi controller binding test failed: ${suffix}`); + } + + try { + await testProposals(identityClient); + } catch (err) { + const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + console.error(`proposals binding test failed: ${suffix}`); + } + + // try { + // await signerTest(); + // } catch (err) { + // const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + // console.error(`signer binding test failed: ${suffix}`); + // } + + // try { + // await testExecuteTransaction(kinesis_client); + // } catch (err) { + // const suffix = err instanceof Error ? `${err.message}; ${err.stack}` : `${err}`; + // console.error(`signer binding test failed: ${suffix}`); + // } + + console.log("done"); +} diff --git a/bindings/wasm/examples/src/0_basic/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts similarity index 100% rename from bindings/wasm/examples/src/0_basic/0_create_did.ts rename to bindings/wasm/identity_wasm/examples/src/0_basic/0_create_did.ts diff --git a/bindings/wasm/examples/src/0_basic/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts similarity index 100% rename from bindings/wasm/examples/src/0_basic/1_update_did.ts rename to bindings/wasm/identity_wasm/examples/src/0_basic/1_update_did.ts diff --git a/bindings/wasm/examples/src/0_basic/2_resolve_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts similarity index 100% rename from bindings/wasm/examples/src/0_basic/2_resolve_did.ts rename to bindings/wasm/identity_wasm/examples/src/0_basic/2_resolve_did.ts diff --git a/bindings/wasm/examples/src/0_basic/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts similarity index 100% rename from bindings/wasm/examples/src/0_basic/3_deactivate_did.ts rename to bindings/wasm/identity_wasm/examples/src/0_basic/3_deactivate_did.ts diff --git a/bindings/wasm/examples/src/0_basic/4_delete_did.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/4_delete_did.ts similarity index 100% rename from bindings/wasm/examples/src/0_basic/4_delete_did.ts rename to bindings/wasm/identity_wasm/examples/src/0_basic/4_delete_did.ts diff --git a/bindings/wasm/examples/src/0_basic/5_create_vc.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts similarity index 100% rename from bindings/wasm/examples/src/0_basic/5_create_vc.ts rename to bindings/wasm/identity_wasm/examples/src/0_basic/5_create_vc.ts diff --git a/bindings/wasm/examples/src/0_basic/6_create_vp.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts similarity index 100% rename from bindings/wasm/examples/src/0_basic/6_create_vp.ts rename to bindings/wasm/identity_wasm/examples/src/0_basic/6_create_vp.ts diff --git a/bindings/wasm/examples/src/0_basic/7_revoke_vc.ts b/bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts similarity index 100% rename from bindings/wasm/examples/src/0_basic/7_revoke_vc.ts rename to bindings/wasm/identity_wasm/examples/src/0_basic/7_revoke_vc.ts diff --git a/bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/0_did_controls_did.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/0_did_controls_did.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/0_did_controls_did.ts diff --git a/bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/1_did_issues_nft.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/1_did_issues_nft.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/1_did_issues_nft.ts diff --git a/bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/2_nft_owns_did.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/2_nft_owns_did.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/2_nft_owns_did.ts diff --git a/bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/3_did_issues_tokens.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/3_did_issues_tokens.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/3_did_issues_tokens.ts diff --git a/bindings/wasm/examples/src/1_advanced/4_custom_resolution.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/4_custom_resolution.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/4_custom_resolution.ts diff --git a/bindings/wasm/examples/src/1_advanced/5_domain_linkage.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/5_domain_linkage.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/5_domain_linkage.ts diff --git a/bindings/wasm/examples/src/1_advanced/6_sd_jwt.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/6_sd_jwt.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/6_sd_jwt.ts diff --git a/bindings/wasm/examples/src/1_advanced/7_status_list_2021.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/7_status_list_2021.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/7_status_list_2021.ts diff --git a/bindings/wasm/examples/src/1_advanced/8_zkp.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/8_zkp.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/8_zkp.ts diff --git a/bindings/wasm/examples/src/1_advanced/9_zkp_revocation.ts b/bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts similarity index 100% rename from bindings/wasm/examples/src/1_advanced/9_zkp_revocation.ts rename to bindings/wasm/identity_wasm/examples/src/1_advanced/9_zkp_revocation.ts diff --git a/bindings/wasm/identity_wasm/examples/src/main.ts b/bindings/wasm/identity_wasm/examples/src/main.ts new file mode 100644 index 0000000000..343f45f1aa --- /dev/null +++ b/bindings/wasm/identity_wasm/examples/src/main.ts @@ -0,0 +1,78 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { testApiCall } from "./0_basic/-1_test_api_call"; +// import { createIdentity } from "./0_basic/0_create_did"; +// import { updateIdentity } from "./0_basic/1_update_did"; +// import { resolveIdentity } from "./0_basic/2_resolve_did"; +// import { deactivateIdentity } from "./0_basic/3_deactivate_did"; +// import { deleteIdentity } from "./0_basic/4_delete_did"; +// import { createVC } from "./0_basic/5_create_vc"; +// import { createVP } from "./0_basic/6_create_vp"; +// import { revokeVC } from "./0_basic/7_revoke_vc"; +// import { didControlsDid } from "./1_advanced/0_did_controls_did"; +// import { didIssuesNft } from "./1_advanced/1_did_issues_nft"; +// import { nftOwnsDid } from "./1_advanced/2_nft_owns_did"; +// import { didIssuesTokens } from "./1_advanced/3_did_issues_tokens"; +// import { customResolution } from "./1_advanced/4_custom_resolution"; +// import { domainLinkage } from "./1_advanced/5_domain_linkage"; +// import { sdJwt } from "./1_advanced/6_sd_jwt"; +// import { statusList2021 } from "./1_advanced/7_status_list_2021"; +// import { zkp } from "./1_advanced/8_zkp"; +// import { zkp_revocation } from "./1_advanced/9_zkp_revocation"; + +async function main() { + // Extract example name. + if (process.argv.length != 3) { + throw "Please specify an example name, e.g. '0_create_did'"; + } + const argument = process.argv[2].toLowerCase(); + + switch (argument) { + case "-1_test_api_call": + return await testApiCall(); + // case "0_create_did": + // return await createIdentity(); + // case "1_update_did": + // return await updateIdentity(); + // case "2_resolve_did": + // return await resolveIdentity(); + // case "3_deactivate_did": + // return await deactivateIdentity(); + // case "4_delete_did": + // return await deleteIdentity(); + // case "5_create_vc": + // return await createVC(); + // case "6_create_vp": + // return await createVP(); + // case "7_revoke_vc": + // return await revokeVC(); + // case "0_did_controls_did": + // return await didControlsDid(); + // case "1_did_issues_nft": + // return await didIssuesNft(); + // case "2_nft_owns_did": + // return await nftOwnsDid(); + // case "3_did_issues_tokens": + // return await didIssuesTokens(); + // case "4_custom_resolution": + // return await customResolution(); + // case "5_domain_linkage": + // return await domainLinkage(); + // case "6_sd_jwt": + // return await sdJwt(); + // case "7_status_list_2021": + // return await statusList2021(); + // case "8_zkp": + // return await zkp(); + // case "9_zkp_revocation": + // return await zkp_revocation(); + default: + throw "Unknown example name: '" + argument + "'"; + } +} + +main() + .catch((error) => { + console.log("Example error:", error); + }); diff --git a/bindings/wasm/examples/src/tests/0_create_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/0_create_did.ts similarity index 100% rename from bindings/wasm/examples/src/tests/0_create_did.ts rename to bindings/wasm/identity_wasm/examples/src/tests/0_create_did.ts diff --git a/bindings/wasm/examples/src/tests/0_did_controls_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/0_did_controls_did.ts similarity index 100% rename from bindings/wasm/examples/src/tests/0_did_controls_did.ts rename to bindings/wasm/identity_wasm/examples/src/tests/0_did_controls_did.ts diff --git a/bindings/wasm/examples/src/tests/1_did_issues_nft.ts b/bindings/wasm/identity_wasm/examples/src/tests/1_did_issues_nft.ts similarity index 100% rename from bindings/wasm/examples/src/tests/1_did_issues_nft.ts rename to bindings/wasm/identity_wasm/examples/src/tests/1_did_issues_nft.ts diff --git a/bindings/wasm/examples/src/tests/1_update_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/1_update_did.ts similarity index 100% rename from bindings/wasm/examples/src/tests/1_update_did.ts rename to bindings/wasm/identity_wasm/examples/src/tests/1_update_did.ts diff --git a/bindings/wasm/examples/src/tests/2_nft_owns_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/2_nft_owns_did.ts similarity index 100% rename from bindings/wasm/examples/src/tests/2_nft_owns_did.ts rename to bindings/wasm/identity_wasm/examples/src/tests/2_nft_owns_did.ts diff --git a/bindings/wasm/examples/src/tests/2_resolve_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/2_resolve_did.ts similarity index 100% rename from bindings/wasm/examples/src/tests/2_resolve_did.ts rename to bindings/wasm/identity_wasm/examples/src/tests/2_resolve_did.ts diff --git a/bindings/wasm/examples/src/tests/3_deactivate_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/3_deactivate_did.ts similarity index 100% rename from bindings/wasm/examples/src/tests/3_deactivate_did.ts rename to bindings/wasm/identity_wasm/examples/src/tests/3_deactivate_did.ts diff --git a/bindings/wasm/examples/src/tests/3_did_issues_tokens.ts b/bindings/wasm/identity_wasm/examples/src/tests/3_did_issues_tokens.ts similarity index 100% rename from bindings/wasm/examples/src/tests/3_did_issues_tokens.ts rename to bindings/wasm/identity_wasm/examples/src/tests/3_did_issues_tokens.ts diff --git a/bindings/wasm/examples/src/tests/4_custom_resolution.ts b/bindings/wasm/identity_wasm/examples/src/tests/4_custom_resolution.ts similarity index 100% rename from bindings/wasm/examples/src/tests/4_custom_resolution.ts rename to bindings/wasm/identity_wasm/examples/src/tests/4_custom_resolution.ts diff --git a/bindings/wasm/examples/src/tests/4_delete_did.ts b/bindings/wasm/identity_wasm/examples/src/tests/4_delete_did.ts similarity index 100% rename from bindings/wasm/examples/src/tests/4_delete_did.ts rename to bindings/wasm/identity_wasm/examples/src/tests/4_delete_did.ts diff --git a/bindings/wasm/examples/src/tests/5_create_vc.ts b/bindings/wasm/identity_wasm/examples/src/tests/5_create_vc.ts similarity index 100% rename from bindings/wasm/examples/src/tests/5_create_vc.ts rename to bindings/wasm/identity_wasm/examples/src/tests/5_create_vc.ts diff --git a/bindings/wasm/examples/src/tests/5_domain_linkage.ts b/bindings/wasm/identity_wasm/examples/src/tests/5_domain_linkage.ts similarity index 100% rename from bindings/wasm/examples/src/tests/5_domain_linkage.ts rename to bindings/wasm/identity_wasm/examples/src/tests/5_domain_linkage.ts diff --git a/bindings/wasm/examples/src/tests/6_create_vp.ts b/bindings/wasm/identity_wasm/examples/src/tests/6_create_vp.ts similarity index 100% rename from bindings/wasm/examples/src/tests/6_create_vp.ts rename to bindings/wasm/identity_wasm/examples/src/tests/6_create_vp.ts diff --git a/bindings/wasm/examples/src/tests/6_sd_jwt.ts b/bindings/wasm/identity_wasm/examples/src/tests/6_sd_jwt.ts similarity index 100% rename from bindings/wasm/examples/src/tests/6_sd_jwt.ts rename to bindings/wasm/identity_wasm/examples/src/tests/6_sd_jwt.ts diff --git a/bindings/wasm/examples/src/tests/7_revoke_vc.ts b/bindings/wasm/identity_wasm/examples/src/tests/7_revoke_vc.ts similarity index 100% rename from bindings/wasm/examples/src/tests/7_revoke_vc.ts rename to bindings/wasm/identity_wasm/examples/src/tests/7_revoke_vc.ts diff --git a/bindings/wasm/examples/src/tests/7_status_list_2021.ts b/bindings/wasm/identity_wasm/examples/src/tests/7_status_list_2021.ts similarity index 100% rename from bindings/wasm/examples/src/tests/7_status_list_2021.ts rename to bindings/wasm/identity_wasm/examples/src/tests/7_status_list_2021.ts diff --git a/bindings/wasm/examples/src/tests/8_zkp.ts b/bindings/wasm/identity_wasm/examples/src/tests/8_zkp.ts similarity index 100% rename from bindings/wasm/examples/src/tests/8_zkp.ts rename to bindings/wasm/identity_wasm/examples/src/tests/8_zkp.ts diff --git a/bindings/wasm/examples/src/tests/9_zkp_revocation.ts b/bindings/wasm/identity_wasm/examples/src/tests/9_zkp_revocation.ts similarity index 100% rename from bindings/wasm/examples/src/tests/9_zkp_revocation.ts rename to bindings/wasm/identity_wasm/examples/src/tests/9_zkp_revocation.ts diff --git a/bindings/wasm/examples/src/util.ts b/bindings/wasm/identity_wasm/examples/src/util.ts similarity index 100% rename from bindings/wasm/examples/src/util.ts rename to bindings/wasm/identity_wasm/examples/src/util.ts diff --git a/bindings/wasm/identity_wasm/examples/tsconfig.web.json b/bindings/wasm/identity_wasm/examples/tsconfig.web.json new file mode 100644 index 0000000000..09b504a14a --- /dev/null +++ b/bindings/wasm/identity_wasm/examples/tsconfig.web.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2020", + "outDir": "./dist/web", + "baseUrl": "./", + "lib": [ + "ES6", + "dom" + ], + "esModuleInterop": true, + "moduleResolution": "node", + "paths": { + "@iota/identity-wasm/node": [ + "../web" + ], + "@iota/sdk-wasm/node": [ + "@iota/sdk-wasm/web" + ] + } + }, + "exclude": [ + "tests" + ] +} diff --git a/bindings/wasm/lib/append_functions.ts b/bindings/wasm/identity_wasm/lib/append_functions.ts similarity index 99% rename from bindings/wasm/lib/append_functions.ts rename to bindings/wasm/identity_wasm/lib/append_functions.ts index 7674a247a6..74fc9977e9 100644 --- a/bindings/wasm/lib/append_functions.ts +++ b/bindings/wasm/identity_wasm/lib/append_functions.ts @@ -1,4 +1,5 @@ import { CoreDID, CoreDocument, IotaDID, IotaDocument, IToCoreDID, IToCoreDocument } from "~identity_wasm"; + type GetCoreDocument = (arg: IToCoreDocument) => CoreDocument; type MaybeGetIotaDocument = (arg: IToCoreDocument) => IotaDocument | void; type GetCoreDidClone = (arg: IToCoreDID) => CoreDID; @@ -8,6 +9,7 @@ declare global { var _maybeGetIotaDocumentInternal: MaybeGetIotaDocument; var _getCoreDidCloneInternal: GetCoreDidClone; } + function _getCoreDocumentInternal(arg: IToCoreDocument): CoreDocument { if (arg instanceof CoreDocument) { return arg._shallowCloneInternal(); diff --git a/bindings/wasm/lib/index.ts b/bindings/wasm/identity_wasm/lib/index.ts similarity index 99% rename from bindings/wasm/lib/index.ts rename to bindings/wasm/identity_wasm/lib/index.ts index dd46503528..e9ab2f00b5 100644 --- a/bindings/wasm/lib/index.ts +++ b/bindings/wasm/identity_wasm/lib/index.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import "./append_functions.js"; + export * from "./iota_identity_client.js"; export * from "./jose"; export * from "./jwk_storage"; diff --git a/bindings/wasm/lib/iota_identity_client.ts b/bindings/wasm/identity_wasm/lib/iota_identity_client.ts similarity index 93% rename from bindings/wasm/lib/iota_identity_client.ts rename to bindings/wasm/identity_wasm/lib/iota_identity_client.ts index c17dd511b9..668d7d7dca 100644 --- a/bindings/wasm/lib/iota_identity_client.ts +++ b/bindings/wasm/identity_wasm/lib/iota_identity_client.ts @@ -134,12 +134,15 @@ export class IotaIdentityClient implements IIotaIdentityClient { }); await this.client.retryUntilIncluded(blockId); - // Extract document with computed AliasId. - const documents = IotaDocument.unpackFromBlock(networkHrp, block); - if (documents.length < 1) { - throw new Error("publishDidOutput: no DID document in transaction payload"); - } - return documents[0]; + // Rewrite the following code when working on [Issue #1445 Replace mocked Identity client with real Identity client] + // + // // Extract document with computed AliasId. + // const documents = IotaDocument.unpackFromBlock(networkHrp, block); + // if (documents.length < 1) { + // throw new Error("publishDidOutput: no DID document in transaction payload"); + // } + // return documents[0]; + return IotaDocument.newWithId(IotaDID.parse("Foo Bar")); } /** Destroy the Alias Output containing the given `did`, sending its tokens to a new Basic Output diff --git a/bindings/wasm/lib/jose/ec_curve.ts b/bindings/wasm/identity_wasm/lib/jose/ec_curve.ts similarity index 100% rename from bindings/wasm/lib/jose/ec_curve.ts rename to bindings/wasm/identity_wasm/lib/jose/ec_curve.ts diff --git a/bindings/wasm/lib/jose/ed_curve.ts b/bindings/wasm/identity_wasm/lib/jose/ed_curve.ts similarity index 100% rename from bindings/wasm/lib/jose/ed_curve.ts rename to bindings/wasm/identity_wasm/lib/jose/ed_curve.ts diff --git a/bindings/wasm/lib/jose/index.ts b/bindings/wasm/identity_wasm/lib/jose/index.ts similarity index 100% rename from bindings/wasm/lib/jose/index.ts rename to bindings/wasm/identity_wasm/lib/jose/index.ts diff --git a/bindings/wasm/lib/jose/jwk_operation.ts b/bindings/wasm/identity_wasm/lib/jose/jwk_operation.ts similarity index 100% rename from bindings/wasm/lib/jose/jwk_operation.ts rename to bindings/wasm/identity_wasm/lib/jose/jwk_operation.ts diff --git a/bindings/wasm/lib/jose/jwk_type.ts b/bindings/wasm/identity_wasm/lib/jose/jwk_type.ts similarity index 100% rename from bindings/wasm/lib/jose/jwk_type.ts rename to bindings/wasm/identity_wasm/lib/jose/jwk_type.ts diff --git a/bindings/wasm/lib/jose/jwk_use.ts b/bindings/wasm/identity_wasm/lib/jose/jwk_use.ts similarity index 100% rename from bindings/wasm/lib/jose/jwk_use.ts rename to bindings/wasm/identity_wasm/lib/jose/jwk_use.ts diff --git a/bindings/wasm/lib/jose/jws_algorithm.ts b/bindings/wasm/identity_wasm/lib/jose/jws_algorithm.ts similarity index 100% rename from bindings/wasm/lib/jose/jws_algorithm.ts rename to bindings/wasm/identity_wasm/lib/jose/jws_algorithm.ts diff --git a/bindings/wasm/lib/jwk_storage.ts b/bindings/wasm/identity_wasm/lib/jwk_storage.ts similarity index 100% rename from bindings/wasm/lib/jwk_storage.ts rename to bindings/wasm/identity_wasm/lib/jwk_storage.ts index 235abcc8ce..97a27b2123 100644 --- a/bindings/wasm/lib/jwk_storage.ts +++ b/bindings/wasm/identity_wasm/lib/jwk_storage.ts @@ -18,10 +18,6 @@ export class JwkMemStore implements JwkStorage { return "Ed25519"; } - private _get_key(keyId: string): Jwk | undefined { - return this._keys.get(keyId); - } - public async generate(keyType: string, algorithm: JwsAlgorithm): Promise { if (keyType !== JwkMemStore.ed25519KeyType()) { throw new Error(`unsupported key type ${keyType}`); @@ -91,6 +87,10 @@ export class JwkMemStore implements JwkStorage { public count(): number { return this._keys.size; } + + private _get_key(keyId: string): Jwk | undefined { + return this._keys.get(keyId); + } } // Encodes a Ed25519 keypair into a Jwk. diff --git a/bindings/wasm/lib/key_id_storage.ts b/bindings/wasm/identity_wasm/lib/key_id_storage.ts similarity index 100% rename from bindings/wasm/lib/key_id_storage.ts rename to bindings/wasm/identity_wasm/lib/key_id_storage.ts diff --git a/bindings/wasm/identity_wasm/lib/tsconfig.json b/bindings/wasm/identity_wasm/lib/tsconfig.json new file mode 100644 index 0000000000..08004d04ef --- /dev/null +++ b/bindings/wasm/identity_wasm/lib/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../tsconfig.node.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "~identity_wasm": [ + "../node/identity_wasm", + "./identity_wasm.js" + ], + "~sdk-wasm": [ + "../node_modules/@iota/sdk-wasm/node", + "@iota/sdk-wasm/node" + ], + "../lib": [ + "." + ] + }, + "outDir": "../node", + "declarationDir": "../node" + } +} diff --git a/bindings/wasm/identity_wasm/lib/tsconfig.web.json b/bindings/wasm/identity_wasm/lib/tsconfig.web.json new file mode 100644 index 0000000000..0d87cb28af --- /dev/null +++ b/bindings/wasm/identity_wasm/lib/tsconfig.web.json @@ -0,0 +1,22 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "~identity_wasm": [ + "../web/identity_wasm", + "./identity_wasm.js" + ], + "~sdk-wasm": [ + "../node_modules/@iota/sdk-wasm/web", + "@iota/sdk-wasm/web" + ], + "../lib": [ + "." + ] + }, + "outDir": "../web", + "declarationDir": "../web", + "module": "ES2020" + } +} diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/identity_wasm/package-lock.json similarity index 89% rename from bindings/wasm/package-lock.json rename to bindings/wasm/identity_wasm/package-lock.json index c6afb8ec91..ca0998324a 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/identity_wasm/package-lock.json @@ -9,7 +9,9 @@ "version": "1.4.0", "license": "Apache-2.0", "dependencies": { + "@iota/iota.js": "file:../../../../iota/sdk/typescript", "@noble/ed25519": "^1.7.3", + "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", "base64-arraybuffer": "^1.0.2", "node-fetch": "^2.6.7" @@ -17,6 +19,7 @@ "devDependencies": { "@transmute/did-key-ed25519": "0.3.0-unstable.9", "@types/mocha": "^9.1.0", + "@types/node": "^22.0.0", "big-integer": "^1.6.51", "copy-webpack-plugin": "^7.0.0", "cypress": "^13.12.0", @@ -26,12 +29,12 @@ "jsdoc-to-markdown": "^7.1.1", "mocha": "^9.2.0", "ts-mocha": "^9.0.2", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "tsconfig-paths": "^4.1.0", "txm": "^8.1.0", - "typedoc": "^0.24.6", - "typedoc-plugin-markdown": "^3.14.0", - "typescript": "^4.7.2", + "typedoc": "^0.27.6", + "typedoc-plugin-markdown": "^4.4.1", + "typescript": "=5.3.3", "wasm-opt": "^1.3.0" }, "engines": { @@ -41,6 +44,55 @@ "@iota/sdk-wasm": "^1.0.4" } }, + "../../../../iota/sdk/typescript": { + "name": "@iota/iota-sdk", + "version": "0.3.0", + "license": "Apache-2.0", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0", + "@iota/bcs": "workspace:*", + "@noble/curves": "^1.4.2", + "@noble/hashes": "^1.4.0", + "@scure/bip32": "^1.4.0", + "@scure/bip39": "^1.3.0", + "@suchipi/femver": "^1.0.0", + "bech32": "^2.0.0", + "gql.tada": "^1.8.2", + "graphql": "^16.9.0", + "tweetnacl": "^1.0.3", + "valibot": "^0.36.0" + }, + "devDependencies": { + "@0no-co/graphqlsp": "^1.12.11", + "@graphql-codegen/add": "^5.0.3", + "@graphql-codegen/cli": "^5.0.2", + "@graphql-codegen/typed-document-node": "^5.0.9", + "@graphql-codegen/typescript": "4.0.9", + "@graphql-codegen/typescript-operations": "^4.2.3", + "@iarna/toml": "^2.2.5", + "@iota/build-scripts": "workspace:^", + "@types/node": "^20.14.10", + "@types/tmp": "^0.2.6", + "@types/ws": "^8.5.10", + "cross-env": "^7.0.3", + "dotenv": "^16.4.5", + "graphql-config": "^5.0.3", + "msw": "^2.3.1", + "tmp": "^0.2.3", + "ts-retry-promise": "^0.8.1", + "typescript": "^5.5.3", + "vite": "^5.3.3", + "vitest": "^2.0.1", + "wait-on": "^7.2.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20" + } + }, + "../../../../iotaledger/iota/sdk/typescript": { + "extraneous": true + }, "node_modules/@babel/parser": { "version": "7.21.1", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz", @@ -175,10 +227,27 @@ "node": ">=10.0.0" } }, + "node_modules/@gerrit0/mini-shiki": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.27.2.tgz", + "integrity": "sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-oniguruma": "^1.27.2", + "@shikijs/types": "^1.27.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@iota/iota.js": { + "resolved": "../../../../iota/sdk/typescript", + "link": true + }, "node_modules/@iota/sdk-wasm": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.0.4.tgz", - "integrity": "sha512-sS+9avq4GFgFbDYNX2+sn8H3+rFYRhJiB7aa22T+xIixt4/VuMaHkWCzZSTnTUXeY92M7D0ABYKCF+OlHSLzxg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.1.3.tgz", + "integrity": "sha512-piyl0B6gcoo7mbmX3QUCyEYtqk6UoCS2cqBYiV7FFz3fmT2DPcQJmcaDvW0nmNh5BbRR9MhPkp3MEerPm6mezA==", + "license": "Apache-2.0", "peer": true, "dependencies": { "class-transformer": "^0.5.1", @@ -195,34 +264,32 @@ "fsevents": "^2.3.2" } }, - "node_modules/@iota/sdk-wasm/node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "side-channel": "^1.0.4" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.0.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@jridgewell/resolve-uri": { @@ -235,24 +302,38 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -282,6 +363,18 @@ } ] }, + "node_modules/@noble/hashes": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", + "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -317,6 +410,35 @@ "node": ">= 8" } }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.27.2.tgz", + "integrity": "sha512-FZYKD1KN7srvpkz4lbGLOYWlyDU4Rd+2RtuKfABTkafAPOFr+J6umfIwY/TzOQqfNtWjL7SAwPAO0dcOraRLaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.27.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@shikijs/types": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.27.2.tgz", + "integrity": "sha512-DM9OWUyjmdYdnKDpaGB/GEn9XkToyK1tqxuqbmc5PV+5K8WjjwfygL3+cIvbkSw2v1ySwHDgqATq/+98pJ4Kyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.1.tgz", + "integrity": "sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==", + "dev": true, + "license": "MIT" + }, "node_modules/@stablelib/binary": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", @@ -510,10 +632,11 @@ } }, "node_modules/@types/eslint": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", - "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@types/estree": "*", @@ -521,10 +644,11 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@types/eslint": "*", @@ -532,12 +656,23 @@ } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true, + "license": "MIT", "peer": true }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -595,9 +730,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.7.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", - "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" + "version": "22.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", + "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/@types/node-fetch": { "version": "2.6.2", @@ -643,163 +782,178 @@ "dev": true }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -808,6 +962,7 @@ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, + "license": "BSD-3-Clause", "peer": true }, "node_modules/@xtuc/long": { @@ -815,6 +970,7 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true, + "license": "Apache-2.0", "peer": true }, "node_modules/abort-controller": { @@ -830,10 +986,11 @@ } }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -841,16 +998,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "peer": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -889,6 +1036,51 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -952,13 +1144,6 @@ "node": ">=8" } }, - "node_modules/ansi-sequence-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", - "dev": true, - "license": "MIT" - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1229,9 +1414,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -1241,14 +1426,19 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -1353,9 +1543,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", "dev": true, "funding": [ { @@ -1365,8 +1555,13 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "CC-BY-4.0", "peer": true }, "node_modules/canonicalize": { @@ -1477,10 +1672,11 @@ } }, "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=6.0" @@ -1505,6 +1701,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT", "peer": true }, "node_modules/clean-stack": { @@ -1885,6 +2082,7 @@ "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.4.tgz", "integrity": "sha512-3xU2t6pZjZy/ORHaCvci5OT1DAboS4UuMMM8NBAizeb2C9qmHt+cgAjXgurazkwkPRdO7ccK39M5ZaPCju0r6A==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "debug": "^4.3.4", @@ -2240,10 +2438,11 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.304", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.304.tgz", - "integrity": "sha512-6c8M+ojPgDIXN2NyfGn8oHASXYnayj+gSEnGeLMKb9zjsySeVB/j7KkNAAG9yDcv8gNlhvFg5REa1N/kQU6pgA==", + "version": "1.5.80", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", + "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==", "dev": true, + "license": "ISC", "peer": true }, "node_modules/emoji-regex": { @@ -2271,10 +2470,11 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -2307,17 +2507,19 @@ } }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -2336,6 +2538,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "license": "BSD-2-Clause", "peer": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2359,6 +2562,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "peer": true, "dependencies": { "estraverse": "^5.2.0" @@ -2372,6 +2576,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=4.0" @@ -2382,6 +2587,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=4.0" @@ -2407,6 +2613,7 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=0.8.x" @@ -2523,6 +2730,24 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", + "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -2833,6 +3058,7 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true, + "license": "BSD-2-Clause", "peer": true }, "node_modules/global-dirs": { @@ -2871,10 +3097,11 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" }, "node_modules/growl": { "version": "1.10.5", @@ -3246,6 +3473,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@types/node": "*", @@ -3384,6 +3612,7 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/json-schema": { @@ -3416,13 +3645,6 @@ "node": ">=6" } }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" - }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -3609,6 +3831,7 @@ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=6.11.5" @@ -4627,10 +4850,11 @@ } }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/normalize-path": { @@ -4810,10 +5034,11 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, + "license": "ISC", "peer": true }, "node_modules/picomatch": { @@ -4889,11 +5114,20 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.10.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "dev": true, "dependencies": { "side-channel": "^1.0.4" }, @@ -5056,9 +5290,10 @@ } }, "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "license": "Apache-2.0", "peer": true }, "node_modules/remark-parse": { @@ -5094,6 +5329,17 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -5215,10 +5461,11 @@ "dev": true }, "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -5300,19 +5547,6 @@ "node": ">=8" } }, - "node_modules/shiki": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", - "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -5573,6 +5807,7 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=6" @@ -5602,14 +5837,15 @@ "dev": true }, "node_modules/terser": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", - "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "dev": true, + "license": "BSD-2-Clause", "peer": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -5621,17 +5857,18 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -5656,21 +5893,84 @@ } }, "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "peer": true, "dependencies": { "randombytes": "^2.1.0" @@ -5681,6 +5981,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/test-value": { @@ -5713,6 +6014,7 @@ "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", "deprecated": "no longer maintained", + "license": "(Unlicense OR Apache-2.0)", "peer": true }, "node_modules/throttleit": { @@ -5890,10 +6192,11 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -6024,38 +6327,39 @@ } }, "node_modules/typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "version": "0.27.6", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.6.tgz", + "integrity": "sha512-oBFRoh2Px6jFx366db0lLlihcalq/JzyCVp7Vaq1yphL/tbgx2e+bkpkCgJPunaPvPwoTOXSwasfklWHm7GfAw==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@gerrit0/mini-shiki": "^1.24.0", "lunr": "^2.3.9", - "marked": "^4.3.0", - "minimatch": "^9.0.0", - "shiki": "^0.14.1" + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "yaml": "^2.6.1" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 14.14" + "node": ">= 18" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x" } }, "node_modules/typedoc-plugin-markdown": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", - "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.4.1.tgz", + "integrity": "sha512-fx23nSCvewI9IR8lzIYtzDphETcgTDuxKcmHKGD4lo36oexC+B1k4NaCOY58Snqb4OlE8OXDAGVcQXYYuLRCNw==", "dev": true, "license": "MIT", - "dependencies": { - "handlebars": "^4.7.7" + "engines": { + "node": ">= 18" }, "peerDependencies": { - "typedoc": ">=0.24.0" + "typedoc": "0.27.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -6068,6 +6372,54 @@ "balanced-match": "^1.0.0" } }, + "node_modules/typedoc/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/typedoc/node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/typedoc/node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/typedoc/node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, "node_modules/typedoc/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -6084,17 +6436,25 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/typedoc/node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, "node_modules/typescript": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", - "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/typical": { @@ -6128,6 +6488,12 @@ "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", "dev": true }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, "node_modules/unified": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", @@ -6179,9 +6545,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -6191,15 +6557,20 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "peer": true, "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -6301,20 +6672,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true, - "license": "MIT" - }, "node_modules/walk-back": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", @@ -6339,10 +6696,11 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -6358,35 +6716,35 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.76.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", - "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -6410,6 +6768,7 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=10.13.0" @@ -6522,6 +6881,19 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs-parser": { "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", @@ -6699,10 +7071,60 @@ "ky-universal": "^0.8.2" } }, + "@gerrit0/mini-shiki": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.27.2.tgz", + "integrity": "sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==", + "dev": true, + "requires": { + "@shikijs/engine-oniguruma": "^1.27.2", + "@shikijs/types": "^1.27.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "@iota/iota.js": { + "version": "file:../../../../iota/sdk/typescript", + "requires": { + "@0no-co/graphqlsp": "^1.12.11", + "@graphql-codegen/add": "^5.0.3", + "@graphql-codegen/cli": "^5.0.2", + "@graphql-codegen/typed-document-node": "^5.0.9", + "@graphql-codegen/typescript": "4.0.9", + "@graphql-codegen/typescript-operations": "^4.2.3", + "@graphql-typed-document-node/core": "^3.2.0", + "@iarna/toml": "^2.2.5", + "@iota/bcs": "workspace:*", + "@iota/build-scripts": "workspace:^", + "@noble/curves": "^1.4.2", + "@noble/hashes": "^1.4.0", + "@scure/bip32": "^1.4.0", + "@scure/bip39": "^1.3.0", + "@suchipi/femver": "^1.0.0", + "@types/node": "^20.14.10", + "@types/tmp": "^0.2.6", + "@types/ws": "^8.5.10", + "bech32": "^2.0.0", + "cross-env": "^7.0.3", + "dotenv": "^16.4.5", + "gql.tada": "^1.8.2", + "graphql": "^16.9.0", + "graphql-config": "^5.0.3", + "msw": "^2.3.1", + "tmp": "^0.2.3", + "ts-retry-promise": "^0.8.1", + "tweetnacl": "^1.0.3", + "typescript": "^5.5.3", + "valibot": "^0.36.0", + "vite": "^5.3.3", + "vitest": "^2.0.1", + "wait-on": "^7.2.0", + "ws": "^8.18.0" + } + }, "@iota/sdk-wasm": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.0.4.tgz", - "integrity": "sha512-sS+9avq4GFgFbDYNX2+sn8H3+rFYRhJiB7aa22T+xIixt4/VuMaHkWCzZSTnTUXeY92M7D0ABYKCF+OlHSLzxg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.1.3.tgz", + "integrity": "sha512-piyl0B6gcoo7mbmX3QUCyEYtqk6UoCS2cqBYiV7FFz3fmT2DPcQJmcaDvW0nmNh5BbRR9MhPkp3MEerPm6mezA==", "peer": true, "requires": { "class-transformer": "^0.5.1", @@ -6712,29 +7134,31 @@ "reflect-metadata": "^0.1.13", "semver": "^7.5.2", "text-encoding": "^0.7.0" - }, - "dependencies": { - "qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "peer": true, - "requires": { - "side-channel": "^1.0.4" - } - } } }, "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "peer": true, "requires": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + } } }, "@jridgewell/resolve-uri": { @@ -6744,21 +7168,34 @@ "dev": true }, "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "peer": true }, "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "peer": true, "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + } } }, "@jridgewell/sourcemap-codec": { @@ -6782,6 +7219,11 @@ "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", "integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==" }, + "@noble/hashes": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", + "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6808,6 +7250,32 @@ "fastq": "^1.6.0" } }, + "@shikijs/engine-oniguruma": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.27.2.tgz", + "integrity": "sha512-FZYKD1KN7srvpkz4lbGLOYWlyDU4Rd+2RtuKfABTkafAPOFr+J6umfIwY/TzOQqfNtWjL7SAwPAO0dcOraRLaQ==", + "dev": true, + "requires": { + "@shikijs/types": "1.27.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "@shikijs/types": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.27.2.tgz", + "integrity": "sha512-DM9OWUyjmdYdnKDpaGB/GEn9XkToyK1tqxuqbmc5PV+5K8WjjwfygL3+cIvbkSw2v1ySwHDgqATq/+98pJ4Kyg==", + "dev": true, + "requires": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "@shikijs/vscode-textmate": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.1.tgz", + "integrity": "sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==", + "dev": true + }, "@stablelib/binary": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", @@ -6989,9 +7457,9 @@ } }, "@types/eslint": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", - "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "peer": true, "requires": { @@ -7000,9 +7468,9 @@ } }, "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "peer": true, "requires": { @@ -7011,12 +7479,21 @@ } }, "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true, "peer": true }, + "@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -7074,9 +7551,12 @@ "dev": true }, "@types/node": { - "version": "18.7.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", - "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" + "version": "22.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", + "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "requires": { + "undici-types": "~6.20.0" + } }, "@types/node-fetch": { "version": "2.6.2", @@ -7122,73 +7602,73 @@ "dev": true }, "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "peer": true, "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, "peer": true }, "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, "peer": true }, "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, "peer": true }, "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "peer": true, "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, "peer": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "peer": true, "requires": { @@ -7196,9 +7676,9 @@ } }, "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "peer": true, "requires": { @@ -7206,79 +7686,79 @@ } }, "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, "peer": true }, "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -7306,19 +7786,11 @@ } }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "peer": true, - "requires": {} - }, "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -7347,6 +7819,38 @@ "uri-js": "^4.2.2" } }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "peer": true, + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + } + } + }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -7392,12 +7896,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, - "ansi-sequence-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", - "dev": true - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -7591,16 +8089,16 @@ "dev": true }, "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "peer": true, "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" } }, "buffer": { @@ -7666,9 +8164,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", "dev": true, "peer": true }, @@ -7749,9 +8247,9 @@ "dev": true }, "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "peer": true }, @@ -8339,9 +8837,9 @@ } }, "electron-to-chromium": { - "version": "1.4.304", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.304.tgz", - "integrity": "sha512-6c8M+ojPgDIXN2NyfGn8oHASXYnayj+gSEnGeLMKb9zjsySeVB/j7KkNAAG9yDcv8gNlhvFg5REa1N/kQU6pgA==", + "version": "1.5.80", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", + "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==", "dev": true, "peer": true }, @@ -8367,9 +8865,9 @@ } }, "enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", "dev": true, "peer": true, "requires": { @@ -8394,16 +8892,16 @@ "dev": true }, "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, "peer": true }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, "escape-string-regexp": { @@ -8559,6 +9057,13 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "fast-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", + "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", + "dev": true, + "peer": true + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -8815,9 +9320,9 @@ } }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "growl": { @@ -9194,12 +9699,6 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, - "jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true - }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -9991,9 +10490,9 @@ } }, "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "peer": true }, @@ -10123,9 +10622,9 @@ "dev": true }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "peer": true }, @@ -10181,11 +10680,16 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true + }, "qs": { "version": "6.10.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "dev": true, "requires": { "side-channel": "^1.0.4" } @@ -10302,9 +10806,9 @@ } }, "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", "peer": true }, "remark-parse": { @@ -10333,6 +10837,13 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "peer": true + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -10416,9 +10927,9 @@ "dev": true }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "requires": { "@types/json-schema": "^7.0.8", @@ -10479,18 +10990,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "shiki": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", - "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", - "dev": true, - "requires": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -10712,14 +11211,14 @@ "dev": true }, "terser": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", - "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "dev": true, "peer": true, "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -10734,34 +11233,77 @@ } }, "terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", "dev": true, "peer": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "dependencies": { "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", "dev": true, "peer": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "peer": true, "requires": { @@ -10928,9 +11470,9 @@ } }, "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -11017,15 +11559,16 @@ "dev": true }, "typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "version": "0.27.6", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.6.tgz", + "integrity": "sha512-oBFRoh2Px6jFx366db0lLlihcalq/JzyCVp7Vaq1yphL/tbgx2e+bkpkCgJPunaPvPwoTOXSwasfklWHm7GfAw==", "dev": true, "requires": { + "@gerrit0/mini-shiki": "^1.24.0", "lunr": "^2.3.9", - "marked": "^4.3.0", - "minimatch": "^9.0.0", - "shiki": "^0.14.1" + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "yaml": "^2.6.1" }, "dependencies": { "brace-expansion": { @@ -11037,6 +11580,41 @@ "balanced-match": "^1.0.0" } }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "requires": { + "uc.micro": "^2.0.0" + } + }, + "markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "requires": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + } + }, + "mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, "minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -11045,22 +11623,26 @@ "requires": { "brace-expansion": "^2.0.1" } + }, + "uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true } } }, "typedoc-plugin-markdown": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", - "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.4.1.tgz", + "integrity": "sha512-fx23nSCvewI9IR8lzIYtzDphETcgTDuxKcmHKGD4lo36oexC+B1k4NaCOY58Snqb4OlE8OXDAGVcQXYYuLRCNw==", "dev": true, - "requires": { - "handlebars": "^4.7.7" - } + "requires": {} }, "typescript": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", - "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true }, "typical": { @@ -11088,6 +11670,11 @@ "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", "dev": true }, + "undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, "unified": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", @@ -11125,14 +11712,14 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "peer": true, "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" } }, "uri-js": { @@ -11211,18 +11798,6 @@ "unist-util-stringify-position": "^3.0.0" } }, - "vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, "walk-back": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", @@ -11240,9 +11815,9 @@ } }, "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, "peer": true, "requires": { @@ -11256,35 +11831,34 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { - "version": "5.76.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", - "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, "peer": true, "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" } }, @@ -11383,6 +11957,12 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true + }, "yargs-parser": { "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", diff --git a/bindings/wasm/package.json b/bindings/wasm/identity_wasm/package.json similarity index 84% rename from bindings/wasm/package.json rename to bindings/wasm/identity_wasm/package.json index 5701fa41dc..5c09529114 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -11,12 +11,12 @@ }, "scripts": { "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", - "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ./build/node && tsc --project ./lib/tsconfig.json && node ./build/replace_paths ./lib/tsconfig.json node", - "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ./build/web && tsc --project ./lib/tsconfig.web.json && node ./build/replace_paths ./lib/tsconfig.web.json web", + "bundle:nodejs": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node identity_wasm && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node identity_wasm", + "bundle:web": "wasm-bindgen target/wasm32-unknown-unknown/release/identity_wasm.wasm --typescript --target web --out-dir web && node ../build/web identity_wasm && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web identity_wasm", "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/identity_wasm_bg.wasm -o node/identity_wasm_bg.wasm", "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": "typedoc && npm run fix_docs", - "build:examples:web": "tsc --project ./examples/tsconfig.web.json && node ./build/replace_paths ./examples/tsconfig.web.json ./examples/dist resolve", + "build:examples:web": "tsc --project ./examples/tsconfig.web.json && node ../build/replace_paths ./examples/tsconfig.web.json ./examples/dist identity_wasm resolve", "build": "npm run build:web && npm run build:nodejs && npm run build:docs", "example:node": "ts-node --project tsconfig.node.json -r tsconfig-paths/register ./examples/src/main.ts", "test": "npm run test:unit:node && npm run test:readme && npm run test:node && test:browser:parallel", @@ -58,6 +58,7 @@ ], "devDependencies": { "@transmute/did-key-ed25519": "0.3.0-unstable.9", + "@types/node": "^22.0.0", "@types/mocha": "^9.1.0", "big-integer": "^1.6.51", "copy-webpack-plugin": "^7.0.0", @@ -68,16 +69,18 @@ "jsdoc-to-markdown": "^7.1.1", "mocha": "^9.2.0", "ts-mocha": "^9.0.2", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "tsconfig-paths": "^4.1.0", "txm": "^8.1.0", - "typedoc": "^0.24.6", - "typedoc-plugin-markdown": "^3.14.0", - "typescript": "^4.7.2", + "typedoc": "^0.27.6", + "typedoc-plugin-markdown": "^4.4.1", + "typescript": "=5.3.3", "wasm-opt": "^1.3.0" }, "dependencies": { + "@iota/iota.js": "file:../../../../iota/sdk/typescript", "@noble/ed25519": "^1.7.3", + "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", "base64-arraybuffer": "^1.0.2", "node-fetch": "^2.6.7" diff --git a/bindings/wasm/proc_typescript/Cargo.toml b/bindings/wasm/identity_wasm/proc_typescript/Cargo.toml similarity index 100% rename from bindings/wasm/proc_typescript/Cargo.toml rename to bindings/wasm/identity_wasm/proc_typescript/Cargo.toml diff --git a/bindings/wasm/proc_typescript/src/lib.rs b/bindings/wasm/identity_wasm/proc_typescript/src/lib.rs similarity index 100% rename from bindings/wasm/proc_typescript/src/lib.rs rename to bindings/wasm/identity_wasm/proc_typescript/src/lib.rs diff --git a/bindings/wasm/rust-toolchain.toml b/bindings/wasm/identity_wasm/rust-toolchain.toml similarity index 83% rename from bindings/wasm/rust-toolchain.toml rename to bindings/wasm/identity_wasm/rust-toolchain.toml index 55ff02013f..ab4db37df0 100644 --- a/bindings/wasm/rust-toolchain.toml +++ b/bindings/wasm/identity_wasm/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.81.0" +channel = "1.84.0" components = ["rustfmt"] targets = ["wasm32-unknown-unknown"] profile = "minimal" diff --git a/bindings/wasm/src/common/imported_document_lock.rs b/bindings/wasm/identity_wasm/src/common/imported_document_lock.rs similarity index 100% rename from bindings/wasm/src/common/imported_document_lock.rs rename to bindings/wasm/identity_wasm/src/common/imported_document_lock.rs diff --git a/bindings/wasm/src/common/mod.rs b/bindings/wasm/identity_wasm/src/common/mod.rs similarity index 100% rename from bindings/wasm/src/common/mod.rs rename to bindings/wasm/identity_wasm/src/common/mod.rs diff --git a/bindings/wasm/src/common/timestamp.rs b/bindings/wasm/identity_wasm/src/common/timestamp.rs similarity index 100% rename from bindings/wasm/src/common/timestamp.rs rename to bindings/wasm/identity_wasm/src/common/timestamp.rs diff --git a/bindings/wasm/src/common/types.rs b/bindings/wasm/identity_wasm/src/common/types.rs similarity index 99% rename from bindings/wasm/src/common/types.rs rename to bindings/wasm/identity_wasm/src/common/types.rs index 8264e923ce..04d9793519 100644 --- a/bindings/wasm/src/common/types.rs +++ b/bindings/wasm/identity_wasm/src/common/types.rs @@ -50,7 +50,6 @@ extern "C" { #[wasm_bindgen(typescript_type = "Service[]")] pub type ArrayService; - } impl TryFrom for MapStringAny { diff --git a/bindings/wasm/src/common/utils.rs b/bindings/wasm/identity_wasm/src/common/utils.rs similarity index 100% rename from bindings/wasm/src/common/utils.rs rename to bindings/wasm/identity_wasm/src/common/utils.rs diff --git a/bindings/wasm/src/credential/credential.rs b/bindings/wasm/identity_wasm/src/credential/credential.rs similarity index 100% rename from bindings/wasm/src/credential/credential.rs rename to bindings/wasm/identity_wasm/src/credential/credential.rs diff --git a/bindings/wasm/src/credential/credential_builder.rs b/bindings/wasm/identity_wasm/src/credential/credential_builder.rs similarity index 100% rename from bindings/wasm/src/credential/credential_builder.rs rename to bindings/wasm/identity_wasm/src/credential/credential_builder.rs diff --git a/bindings/wasm/src/credential/domain_linkage_configuration.rs b/bindings/wasm/identity_wasm/src/credential/domain_linkage_configuration.rs similarity index 100% rename from bindings/wasm/src/credential/domain_linkage_configuration.rs rename to bindings/wasm/identity_wasm/src/credential/domain_linkage_configuration.rs diff --git a/bindings/wasm/src/credential/domain_linkage_credential_builder.rs b/bindings/wasm/identity_wasm/src/credential/domain_linkage_credential_builder.rs similarity index 100% rename from bindings/wasm/src/credential/domain_linkage_credential_builder.rs rename to bindings/wasm/identity_wasm/src/credential/domain_linkage_credential_builder.rs diff --git a/bindings/wasm/src/credential/domain_linkage_validator.rs b/bindings/wasm/identity_wasm/src/credential/domain_linkage_validator.rs similarity index 100% rename from bindings/wasm/src/credential/domain_linkage_validator.rs rename to bindings/wasm/identity_wasm/src/credential/domain_linkage_validator.rs diff --git a/bindings/wasm/src/credential/jpt.rs b/bindings/wasm/identity_wasm/src/credential/jpt.rs similarity index 100% rename from bindings/wasm/src/credential/jpt.rs rename to bindings/wasm/identity_wasm/src/credential/jpt.rs diff --git a/bindings/wasm/src/credential/jpt_credential_validator/decoded_jpt_credential.rs b/bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/decoded_jpt_credential.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_credential_validator/decoded_jpt_credential.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/decoded_jpt_credential.rs diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validation_options.rs b/bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jpt_credential_validation_options.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validation_options.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jpt_credential_validation_options.rs diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator.rs b/bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jpt_credential_validator.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jpt_credential_validator.rs diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator_utils.rs b/bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jpt_credential_validator_utils.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_credential_validator/jpt_credential_validator_utils.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jpt_credential_validator_utils.rs diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jwp_credential_options.rs b/bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jwp_credential_options.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_credential_validator/jwp_credential_options.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jwp_credential_options.rs diff --git a/bindings/wasm/src/credential/jpt_credential_validator/jwp_verification_options.rs b/bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jwp_verification_options.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_credential_validator/jwp_verification_options.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/jwp_verification_options.rs diff --git a/bindings/wasm/src/credential/jpt_credential_validator/mod.rs b/bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/mod.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_credential_validator/mod.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_credential_validator/mod.rs diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/decoded_jpt_presentation.rs b/bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/decoded_jpt_presentation.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_presentiation_validation/decoded_jpt_presentation.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/decoded_jpt_presentation.rs diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validation_options.rs b/bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validation_options.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validation_options.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validation_options.rs diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator.rs b/bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator.rs diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator_utils.rs b/bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator_utils.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator_utils.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/jpt_presentation_validator_utils.rs diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/jwp_presentation_options.rs b/bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/jwp_presentation_options.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_presentiation_validation/jwp_presentation_options.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/jwp_presentation_options.rs diff --git a/bindings/wasm/src/credential/jpt_presentiation_validation/mod.rs b/bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/mod.rs similarity index 100% rename from bindings/wasm/src/credential/jpt_presentiation_validation/mod.rs rename to bindings/wasm/identity_wasm/src/credential/jpt_presentiation_validation/mod.rs diff --git a/bindings/wasm/src/credential/jws.rs b/bindings/wasm/identity_wasm/src/credential/jws.rs similarity index 100% rename from bindings/wasm/src/credential/jws.rs rename to bindings/wasm/identity_wasm/src/credential/jws.rs diff --git a/bindings/wasm/src/credential/jwt.rs b/bindings/wasm/identity_wasm/src/credential/jwt.rs similarity index 100% rename from bindings/wasm/src/credential/jwt.rs rename to bindings/wasm/identity_wasm/src/credential/jwt.rs diff --git a/bindings/wasm/src/credential/jwt_credential_validation/decoded_jwt_credential.rs b/bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/decoded_jwt_credential.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_credential_validation/decoded_jwt_credential.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/decoded_jwt_credential.rs diff --git a/bindings/wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs b/bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/jwt_credential_validator.rs diff --git a/bindings/wasm/src/credential/jwt_credential_validation/kb_validation_options.rs b/bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/kb_validation_options.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_credential_validation/kb_validation_options.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/kb_validation_options.rs diff --git a/bindings/wasm/src/credential/jwt_credential_validation/mod.rs b/bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/mod.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_credential_validation/mod.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/mod.rs diff --git a/bindings/wasm/src/credential/jwt_credential_validation/options.rs b/bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/options.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_credential_validation/options.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/options.rs diff --git a/bindings/wasm/src/credential/jwt_credential_validation/sd_jwt_validator.rs b/bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/sd_jwt_validator.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_credential_validation/sd_jwt_validator.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/sd_jwt_validator.rs diff --git a/bindings/wasm/src/credential/jwt_credential_validation/unknown_credential.rs b/bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/unknown_credential.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_credential_validation/unknown_credential.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_credential_validation/unknown_credential.rs diff --git a/bindings/wasm/src/credential/jwt_presentation_validation/decoded_jwt_presentation.rs b/bindings/wasm/identity_wasm/src/credential/jwt_presentation_validation/decoded_jwt_presentation.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_presentation_validation/decoded_jwt_presentation.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_presentation_validation/decoded_jwt_presentation.rs diff --git a/bindings/wasm/src/credential/jwt_presentation_validation/jwt_presentation_validator.rs b/bindings/wasm/identity_wasm/src/credential/jwt_presentation_validation/jwt_presentation_validator.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_presentation_validation/jwt_presentation_validator.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_presentation_validation/jwt_presentation_validator.rs diff --git a/bindings/wasm/src/credential/jwt_presentation_validation/mod.rs b/bindings/wasm/identity_wasm/src/credential/jwt_presentation_validation/mod.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_presentation_validation/mod.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_presentation_validation/mod.rs diff --git a/bindings/wasm/src/credential/jwt_presentation_validation/options.rs b/bindings/wasm/identity_wasm/src/credential/jwt_presentation_validation/options.rs similarity index 100% rename from bindings/wasm/src/credential/jwt_presentation_validation/options.rs rename to bindings/wasm/identity_wasm/src/credential/jwt_presentation_validation/options.rs diff --git a/bindings/wasm/src/credential/linked_domain_service.rs b/bindings/wasm/identity_wasm/src/credential/linked_domain_service.rs similarity index 100% rename from bindings/wasm/src/credential/linked_domain_service.rs rename to bindings/wasm/identity_wasm/src/credential/linked_domain_service.rs diff --git a/bindings/wasm/src/credential/linked_verifiable_presentation_service.rs b/bindings/wasm/identity_wasm/src/credential/linked_verifiable_presentation_service.rs similarity index 100% rename from bindings/wasm/src/credential/linked_verifiable_presentation_service.rs rename to bindings/wasm/identity_wasm/src/credential/linked_verifiable_presentation_service.rs diff --git a/bindings/wasm/src/credential/mod.rs b/bindings/wasm/identity_wasm/src/credential/mod.rs similarity index 100% rename from bindings/wasm/src/credential/mod.rs rename to bindings/wasm/identity_wasm/src/credential/mod.rs diff --git a/bindings/wasm/src/credential/options.rs b/bindings/wasm/identity_wasm/src/credential/options.rs similarity index 100% rename from bindings/wasm/src/credential/options.rs rename to bindings/wasm/identity_wasm/src/credential/options.rs diff --git a/bindings/wasm/src/credential/presentation/mod.rs b/bindings/wasm/identity_wasm/src/credential/presentation/mod.rs similarity index 100% rename from bindings/wasm/src/credential/presentation/mod.rs rename to bindings/wasm/identity_wasm/src/credential/presentation/mod.rs diff --git a/bindings/wasm/src/credential/presentation/presentation.rs b/bindings/wasm/identity_wasm/src/credential/presentation/presentation.rs similarity index 100% rename from bindings/wasm/src/credential/presentation/presentation.rs rename to bindings/wasm/identity_wasm/src/credential/presentation/presentation.rs diff --git a/bindings/wasm/src/credential/presentation/presentation_builder.rs b/bindings/wasm/identity_wasm/src/credential/presentation/presentation_builder.rs similarity index 100% rename from bindings/wasm/src/credential/presentation/presentation_builder.rs rename to bindings/wasm/identity_wasm/src/credential/presentation/presentation_builder.rs diff --git a/bindings/wasm/src/credential/proof.rs b/bindings/wasm/identity_wasm/src/credential/proof.rs similarity index 100% rename from bindings/wasm/src/credential/proof.rs rename to bindings/wasm/identity_wasm/src/credential/proof.rs diff --git a/bindings/wasm/src/credential/revocation/mod.rs b/bindings/wasm/identity_wasm/src/credential/revocation/mod.rs similarity index 100% rename from bindings/wasm/src/credential/revocation/mod.rs rename to bindings/wasm/identity_wasm/src/credential/revocation/mod.rs diff --git a/bindings/wasm/src/credential/revocation/status_list_2021/credential.rs b/bindings/wasm/identity_wasm/src/credential/revocation/status_list_2021/credential.rs similarity index 100% rename from bindings/wasm/src/credential/revocation/status_list_2021/credential.rs rename to bindings/wasm/identity_wasm/src/credential/revocation/status_list_2021/credential.rs diff --git a/bindings/wasm/src/credential/revocation/status_list_2021/entry.rs b/bindings/wasm/identity_wasm/src/credential/revocation/status_list_2021/entry.rs similarity index 100% rename from bindings/wasm/src/credential/revocation/status_list_2021/entry.rs rename to bindings/wasm/identity_wasm/src/credential/revocation/status_list_2021/entry.rs diff --git a/bindings/wasm/src/credential/revocation/status_list_2021/mod.rs b/bindings/wasm/identity_wasm/src/credential/revocation/status_list_2021/mod.rs similarity index 100% rename from bindings/wasm/src/credential/revocation/status_list_2021/mod.rs rename to bindings/wasm/identity_wasm/src/credential/revocation/status_list_2021/mod.rs diff --git a/bindings/wasm/src/credential/revocation/status_list_2021/status_list.rs b/bindings/wasm/identity_wasm/src/credential/revocation/status_list_2021/status_list.rs similarity index 100% rename from bindings/wasm/src/credential/revocation/status_list_2021/status_list.rs rename to bindings/wasm/identity_wasm/src/credential/revocation/status_list_2021/status_list.rs diff --git a/bindings/wasm/src/credential/revocation/validity_timeframe_2024/mod.rs b/bindings/wasm/identity_wasm/src/credential/revocation/validity_timeframe_2024/mod.rs similarity index 100% rename from bindings/wasm/src/credential/revocation/validity_timeframe_2024/mod.rs rename to bindings/wasm/identity_wasm/src/credential/revocation/validity_timeframe_2024/mod.rs diff --git a/bindings/wasm/src/credential/revocation/validity_timeframe_2024/status.rs b/bindings/wasm/identity_wasm/src/credential/revocation/validity_timeframe_2024/status.rs similarity index 100% rename from bindings/wasm/src/credential/revocation/validity_timeframe_2024/status.rs rename to bindings/wasm/identity_wasm/src/credential/revocation/validity_timeframe_2024/status.rs diff --git a/bindings/wasm/src/credential/types.rs b/bindings/wasm/identity_wasm/src/credential/types.rs similarity index 100% rename from bindings/wasm/src/credential/types.rs rename to bindings/wasm/identity_wasm/src/credential/types.rs diff --git a/bindings/wasm/src/did/did_jwk.rs b/bindings/wasm/identity_wasm/src/did/did_jwk.rs similarity index 100% rename from bindings/wasm/src/did/did_jwk.rs rename to bindings/wasm/identity_wasm/src/did/did_jwk.rs diff --git a/bindings/wasm/src/did/jws_verification_options.rs b/bindings/wasm/identity_wasm/src/did/jws_verification_options.rs similarity index 100% rename from bindings/wasm/src/did/jws_verification_options.rs rename to bindings/wasm/identity_wasm/src/did/jws_verification_options.rs diff --git a/bindings/wasm/src/did/mod.rs b/bindings/wasm/identity_wasm/src/did/mod.rs similarity index 100% rename from bindings/wasm/src/did/mod.rs rename to bindings/wasm/identity_wasm/src/did/mod.rs diff --git a/bindings/wasm/src/did/service.rs b/bindings/wasm/identity_wasm/src/did/service.rs similarity index 100% rename from bindings/wasm/src/did/service.rs rename to bindings/wasm/identity_wasm/src/did/service.rs diff --git a/bindings/wasm/src/did/wasm_core_did.rs b/bindings/wasm/identity_wasm/src/did/wasm_core_did.rs similarity index 99% rename from bindings/wasm/src/did/wasm_core_did.rs rename to bindings/wasm/identity_wasm/src/did/wasm_core_did.rs index 292cb0bb93..1703c00c0d 100644 --- a/bindings/wasm/src/did/wasm_core_did.rs +++ b/bindings/wasm/identity_wasm/src/did/wasm_core_did.rs @@ -151,7 +151,6 @@ extern "C" { // or {@link IotaDID}. #[wasm_bindgen(js_name = _getCoreDidCloneInternal, skip_typescript)] pub fn get_core_did_clone(input: &IToCoreDID) -> WasmCoreDID; - } #[wasm_bindgen(typescript_custom_section)] diff --git a/bindings/wasm/src/did/wasm_core_document.rs b/bindings/wasm/identity_wasm/src/did/wasm_core_document.rs similarity index 98% rename from bindings/wasm/src/did/wasm_core_document.rs rename to bindings/wasm/identity_wasm/src/did/wasm_core_document.rs index fd66c4e7ca..d656f361b4 100644 --- a/bindings/wasm/src/did/wasm_core_document.rs +++ b/bindings/wasm/identity_wasm/src/did/wasm_core_document.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::rc::Rc; +use std::sync::Arc; use super::WasmCoreDID; use super::WasmJwsVerificationOptions; @@ -630,7 +631,7 @@ impl WasmCoreDocument { ) -> Result { let alg: JwsAlgorithm = alg.into_serde().wasm_result()?; let document_lock_clone: Rc = self.0.clone(); - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let scope: MethodScope = scope.0; let promise: Promise = future_to_promise(async move { let method_fragment: String = document_lock_clone @@ -648,7 +649,7 @@ impl WasmCoreDocument { /// the `storage`. #[wasm_bindgen(js_name = purgeMethod)] pub fn purge_method(&mut self, storage: &WasmStorage, id: &WasmDIDUrl) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let document_lock_clone: Rc = self.0.clone(); let id: DIDUrl = id.0.clone(); let promise: Promise = future_to_promise(async move { @@ -676,7 +677,7 @@ impl WasmCoreDocument { payload: String, options: &WasmJwsSignatureOptions, ) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options_clone: JwsSignatureOptions = options.0.clone(); let document_lock_clone: Rc = self.0.clone(); let promise: Promise = future_to_promise(async move { @@ -709,7 +710,7 @@ impl WasmCoreDocument { options: &WasmJwsSignatureOptions, custom_claims: Option, ) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options_clone: JwsSignatureOptions = options.0.clone(); let document_lock_clone: Rc = self.0.clone(); let credential_clone: Credential = credential.0.clone(); @@ -744,7 +745,7 @@ impl WasmCoreDocument { signature_options: &WasmJwsSignatureOptions, presentation_options: &WasmJwtPresentationOptions, ) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options_clone: JwsSignatureOptions = signature_options.0.clone(); let document_lock_clone: Rc = self.0.clone(); let presentation_clone: Presentation = presentation.0.clone(); diff --git a/bindings/wasm/src/did/wasm_did_url.rs b/bindings/wasm/identity_wasm/src/did/wasm_did_url.rs similarity index 100% rename from bindings/wasm/src/did/wasm_did_url.rs rename to bindings/wasm/identity_wasm/src/did/wasm_did_url.rs diff --git a/bindings/wasm/src/error.rs b/bindings/wasm/identity_wasm/src/error.rs similarity index 90% rename from bindings/wasm/src/error.rs rename to bindings/wasm/identity_wasm/src/error.rs index 035e7838bf..2bc1a861a4 100644 --- a/bindings/wasm/src/error.rs +++ b/bindings/wasm/identity_wasm/src/error.rs @@ -108,7 +108,8 @@ impl_wasm_error_from!( identity_iota::sd_jwt_payload::Error, identity_iota::credential::KeyBindingJwtError, identity_iota::credential::status_list_2021::StatusListError, - identity_iota::credential::status_list_2021::StatusList2021CredentialError + identity_iota::credential::status_list_2021::StatusList2021CredentialError, + identity_iota::iota::rebased::Error ); // Similar to `impl_wasm_error_from`, but uses the types name instead of requiring/calling Into &'static str @@ -175,8 +176,8 @@ impl From for WasmError<'_> { } } -impl From for WasmError<'_> { - fn from(error: identity_iota::iota::block::Error) -> Self { +impl From for WasmError<'_> { + fn from(error: iota_sdk::types::block::Error) -> Self { Self { name: Cow::Borrowed("iota_sdk::types::block::Error"), message: Cow::Owned(error.to_string()), @@ -265,6 +266,24 @@ impl From for WasmError<'_> { } } +impl From for WasmError<'_> { + fn from(error: serde_wasm_bindgen::Error) -> Self { + Self { + name: Cow::Borrowed("serde_wasm_bindgen::Error"), + message: Cow::Owned(ErrorMessage(&error).to_string()), + } + } +} + +impl From for WasmError<'_> { + fn from(error: secret_storage::Error) -> Self { + Self { + name: Cow::Borrowed("secret_storage::Error"), + message: Cow::Owned(ErrorMessage(&error).to_string()), + } + } +} + /// Convenience struct to convert Result to errors in the Rust library. pub struct JsValueResult(pub(crate) Result); @@ -291,6 +310,12 @@ impl JsValueResult { pub fn to_iota_core_error(self) -> StdResult { self.stringify_error().map_err(identity_iota::iota::Error::JsError) } + + pub fn to_kinesis_client_error(self) -> StdResult { + self + .stringify_error() + .map_err(|e| identity_iota::iota::rebased::Error::FfiError(e.to_string())) + } } /// Consumes the struct and returns a Result<_, String>, leaving an `Ok` value untouched. @@ -331,3 +356,13 @@ impl serde::Deserialize<'a>> From for KeyIdStorageResu }) } } + +impl serde::Deserialize<'a>> From for StdResult { + fn from(result: JsValueResult) -> Self { + result.to_kinesis_client_error().and_then(|js_value| { + js_value + .into_serde() + .map_err(|e| identity_iota::iota::rebased::Error::FfiError(e.to_string())) + }) + } +} diff --git a/bindings/wasm/src/iota/identity_client.rs b/bindings/wasm/identity_wasm/src/iota/identity_client.rs similarity index 90% rename from bindings/wasm/src/iota/identity_client.rs rename to bindings/wasm/identity_wasm/src/iota/identity_client.rs index 5076a877da..8061852039 100644 --- a/bindings/wasm/src/iota/identity_client.rs +++ b/bindings/wasm/identity_wasm/src/iota/identity_client.rs @@ -4,18 +4,18 @@ use core::fmt::Debug; use core::fmt::Formatter; -use identity_iota::iota::block::output::dto::AliasOutputDto; -use identity_iota::iota::block::output::AliasId; -use identity_iota::iota::block::output::AliasOutput; -use identity_iota::iota::block::output::OutputId; -use identity_iota::iota::block::protocol::ProtocolParameters; -use identity_iota::iota::block::TryFromDto; -use identity_iota::iota::IotaIdentityClient; +use iota_sdk::types::block::output::dto::AliasOutputDto; +use iota_sdk::types::block::output::AliasId; +use iota_sdk::types::block::output::AliasOutput; +use iota_sdk::types::block::output::OutputId; +use iota_sdk::types::block::protocol::ProtocolParameters; +use iota_sdk::types::TryFromDto; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use crate::error::JsValueResult; +use crate::obsolete::IotaIdentityClient; #[wasm_bindgen] extern "C" { diff --git a/bindings/wasm/src/iota/identity_client_ext.rs b/bindings/wasm/identity_wasm/src/iota/identity_client_ext.rs similarity index 95% rename from bindings/wasm/src/iota/identity_client_ext.rs rename to bindings/wasm/identity_wasm/src/iota/identity_client_ext.rs index cdb0ae7ec4..2a9fbccb6b 100644 --- a/bindings/wasm/src/iota/identity_client_ext.rs +++ b/bindings/wasm/identity_wasm/src/iota/identity_client_ext.rs @@ -1,14 +1,13 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota::iota::block::address::dto::AddressDto; -use identity_iota::iota::block::address::Address; -use identity_iota::iota::block::output::dto::AliasOutputDto; -use identity_iota::iota::block::output::AliasOutput; -use identity_iota::iota::block::output::RentStructure; use identity_iota::iota::IotaDID; use identity_iota::iota::IotaDocument; -use identity_iota::iota::IotaIdentityClientExt; +use iota_sdk::types::block::address::dto::AddressDto; +use iota_sdk::types::block::address::Address; +use iota_sdk::types::block::output::dto::AliasOutputDto; +use iota_sdk::types::block::output::AliasOutput; +use iota_sdk::types::block::output::RentStructure; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -17,6 +16,8 @@ use wasm_bindgen_futures::future_to_promise; use crate::error::Result; use crate::error::WasmResult; use crate::iota::identity_client::WasmIotaIdentityClient; +use crate::obsolete::IotaIdentityClientExt; + use crate::iota::WasmIotaDID; use crate::iota::WasmIotaDocument; diff --git a/bindings/wasm/src/iota/iota_did.rs b/bindings/wasm/identity_wasm/src/iota/iota_did.rs similarity index 95% rename from bindings/wasm/src/iota/iota_did.rs rename to bindings/wasm/identity_wasm/src/iota/iota_did.rs index cbf126b80d..2c11537241 100644 --- a/bindings/wasm/src/iota/iota_did.rs +++ b/bindings/wasm/identity_wasm/src/iota/iota_did.rs @@ -3,7 +3,6 @@ use identity_iota::did::Error as DIDError; use identity_iota::did::DID; -use identity_iota::iota::block::output::AliasId; use identity_iota::iota::IotaDID; use identity_iota::iota::NetworkName; use wasm_bindgen::prelude::*; @@ -55,9 +54,9 @@ impl WasmIotaDID { /// network name. #[wasm_bindgen(js_name = fromAliasId)] #[allow(non_snake_case)] - pub fn from_alias_id(aliasId: String, network: String) -> Result { + pub fn from_object_id(objectId: String, network: String) -> Result { let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; - Ok(Self::from(IotaDID::from_alias_id(aliasId.as_ref(), &network_name))) + Ok(Self::from(IotaDID::from_object_id(objectId.as_ref(), &network_name))) } /// Creates a new placeholder {@link IotaDID} with the given network name. @@ -155,8 +154,8 @@ impl WasmIotaDID { /// Returns the hex-encoded AliasId with a '0x' prefix, from the DID tag. #[wasm_bindgen(js_name = toAliasId)] - pub fn to_alias_id(&self) -> String { - AliasId::from(&self.0).to_string() + pub fn to_object_id(&self) -> String { + self.0.to_string() } /// Converts the `DID` into a {@link DIDUrl}, consuming it. diff --git a/bindings/wasm/src/iota/iota_document.rs b/bindings/wasm/identity_wasm/src/iota/iota_document.rs similarity index 90% rename from bindings/wasm/src/iota/iota_document.rs rename to bindings/wasm/identity_wasm/src/iota/iota_document.rs index 1747f82e6e..3716bb580d 100644 --- a/bindings/wasm/src/iota/iota_document.rs +++ b/bindings/wasm/identity_wasm/src/iota/iota_document.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::rc::Rc; +use std::sync::Arc; use identity_iota::core::Object; use identity_iota::core::OneOrMany; @@ -14,9 +15,6 @@ use identity_iota::credential::JwtPresentationOptions; use identity_iota::credential::Presentation; use identity_iota::did::DIDUrl; -use identity_iota::iota::block::output::dto::AliasOutputDto; -use identity_iota::iota::block::output::AliasOutput; -use identity_iota::iota::block::TryFromDto; use identity_iota::iota::IotaDID; use identity_iota::iota::IotaDocument; use identity_iota::iota::NetworkName; @@ -27,6 +25,7 @@ use identity_iota::storage::storage::JwsSignatureOptions; use identity_iota::verification::jose::jws::JwsAlgorithm; use identity_iota::verification::MethodScope; use identity_iota::verification::VerificationMethod; +use iota_sdk::types::TryFromDto; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -62,7 +61,6 @@ use crate::did::WasmJwsVerificationOptions; use crate::did::WasmService; use crate::error::Result; use crate::error::WasmResult; -use crate::iota::identity_client_ext::WasmAliasOutput; use crate::iota::WasmIotaDID; use crate::iota::WasmIotaDocumentMetadata; use crate::iota::WasmStateMetadataEncoding; @@ -437,63 +435,69 @@ impl WasmIotaDocument { .wasm_result() } - /// Deserializes the document from an Alias Output. - /// - /// If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` - /// if `stateMetadata` is empty. - /// - /// The `tokenSupply` must be equal to the token supply of the network the DID is associated with. - /// - /// NOTE: `did` is required since it is omitted from the serialized DID Document and - /// cannot be inferred from the state metadata. It also indicates the network, which is not - /// encoded in the `AliasId` alone. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = unpackFromOutput)] - pub fn unpack_from_output( - did: &WasmIotaDID, - aliasOutput: WasmAliasOutput, - allowEmpty: bool, - ) -> Result { - let alias_dto: AliasOutputDto = aliasOutput.into_serde().wasm_result()?; - let alias_output: AliasOutput = AliasOutput::try_from_dto(alias_dto) - .map_err(|err| { - identity_iota::iota::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {err}")) - }) - .wasm_result()?; - IotaDocument::unpack_from_output(&did.0, &alias_output, allowEmpty) - .map(WasmIotaDocument::from) - .wasm_result() - } - - /// Returns all DID documents of the Alias Outputs contained in the block's transaction payload - /// outputs, if any. - /// - /// Errors if any Alias Output does not contain a valid or empty DID Document. - #[allow(non_snake_case)] - #[wasm_bindgen(js_name = unpackFromBlock)] - pub fn unpack_from_block(network: String, block: &WasmBlock) -> Result { - let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; - let block_dto: identity_iota::iota::block::BlockDto = block - .into_serde() - .map_err(|err| { - identity_iota::iota::Error::JsError(format!("unpackFromBlock failed to deserialize BlockDto: {err}")) - }) - .wasm_result()?; - - let block: identity_iota::iota::block::Block = identity_iota::iota::block::Block::try_from_dto(block_dto) - .map_err(|err| identity_iota::iota::Error::JsError(format!("unpackFromBlock failed to convert BlockDto: {err}"))) - .wasm_result()?; - - Ok( - IotaDocument::unpack_from_block(&network_name, &block) - .wasm_result()? - .into_iter() - .map(WasmIotaDocument::from) - .map(JsValue::from) - .collect::() - .unchecked_into::(), - ) - } + // Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] + // + // /// Deserializes the document from an Alias Output. + // /// + // /// If `allowEmpty` is true, this will return an empty DID document marked as `deactivated` + // /// if `stateMetadata` is empty. + // /// + // /// The `tokenSupply` must be equal to the token supply of the network the DID is associated with. + // /// + // /// NOTE: `did` is required since it is omitted from the serialized DID Document and + // /// cannot be inferred from the state metadata. It also indicates the network, which is not + // /// encoded in the `AliasId` alone. + // #[allow(non_snake_case)] + // #[wasm_bindgen(js_name = unpackFromOutput)] + // pub fn unpack_from_output( + // did: &WasmIotaDID, + // aliasOutput: WasmAliasOutput, + // allowEmpty: bool, + // ) -> Result { + // let alias_dto: AliasOutputDto = aliasOutput.into_serde().wasm_result()?; + // let alias_output: AliasOutput = AliasOutput::try_from_dto(alias_dto) + // .map_err(|err| { + // identity_iota::iota::Error::JsError(format!("get_alias_output failed to convert AliasOutputDto: {err}")) + // }) + // .wasm_result()?; + // IotaDocument::unpack_from_output(&did.0, &alias_output, allowEmpty) + // .map(WasmIotaDocument::from) + // .wasm_result() + // } + + // Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] + // + // /// Returns all DID documents of the Alias Outputs contained in the block's transaction payload + // /// outputs, if any. + // /// + // /// Errors if any Alias Output does not contain a valid or empty DID Document. + // #[allow(non_snake_case)] + // #[wasm_bindgen(js_name = unpackFromBlock)] + // pub fn unpack_from_block(network: String, block: &WasmBlock) -> Result { + // let network_name: NetworkName = NetworkName::try_from(network).wasm_result()?; + // let block_dto: iota_sdk::types::block::BlockDto = block + // .into_serde() + // .map_err(|err| { + // identity_iota::iota::Error::JsError(format!("unpackFromBlock failed to deserialize BlockDto: {err}")) + // }) + // .wasm_result()?; + // + // let block: iota_sdk::types::block::Block = iota_sdk::types::block::Block::try_from_dto(block_dto) + // .map_err(|err| identity_iota::iota::Error::JsError( + // format!("unpackFromBlock failed to convert BlockDto: {err}") + // )) + // .wasm_result()?; + // + // Ok( + // IotaDocument::unpack_from_block(&network_name, &block) + // .wasm_result()? + // .into_iter() + // .map(WasmIotaDocument::from) + // .map(JsValue::from) + // .collect::() + // .unchecked_into::(), + // ) + // } // =========================================================================== // Metadata @@ -691,7 +695,7 @@ impl WasmIotaDocument { ) -> Result { let alg: JwsAlgorithm = alg.into_serde().wasm_result()?; let document_lock_clone: Rc = self.0.clone(); - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let scope: MethodScope = scope.0; let promise: Promise = future_to_promise(async move { let method_fragment: String = document_lock_clone @@ -709,7 +713,7 @@ impl WasmIotaDocument { /// the given `storage`. #[wasm_bindgen(js_name = purgeMethod)] pub fn purge_method(&mut self, storage: &WasmStorage, id: &WasmDIDUrl) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let document_lock_clone: Rc = self.0.clone(); let id: DIDUrl = id.0.clone(); let promise: Promise = future_to_promise(async move { @@ -740,7 +744,7 @@ impl WasmIotaDocument { payload: String, options: &WasmJwsSignatureOptions, ) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options_clone: JwsSignatureOptions = options.0.clone(); let document_lock_clone: Rc = self.0.clone(); let promise: Promise = future_to_promise(async move { @@ -769,7 +773,7 @@ impl WasmIotaDocument { payload: String, options: &WasmJwsSignatureOptions, ) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options_clone: JwsSignatureOptions = options.0.clone(); let document_lock_clone: Rc = self.0.clone(); let promise: Promise = future_to_promise(async move { @@ -802,7 +806,7 @@ impl WasmIotaDocument { options: &WasmJwsSignatureOptions, custom_claims: Option, ) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options_clone: JwsSignatureOptions = options.0.clone(); let document_lock_clone: Rc = self.0.clone(); let credential_clone: Credential = credential.0.clone(); @@ -837,7 +841,7 @@ impl WasmIotaDocument { signature_options: &WasmJwsSignatureOptions, presentation_options: &WasmJwtPresentationOptions, ) -> Result { - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options_clone: JwsSignatureOptions = signature_options.0.clone(); let document_lock_clone: Rc = self.0.clone(); let presentation_clone: Presentation = presentation.0.clone(); @@ -870,7 +874,7 @@ impl WasmIotaDocument { scope: WasmMethodScope, ) -> Result { let document_lock_clone: Rc = self.0.clone(); - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let promise: Promise = future_to_promise(async move { let method_fragment: String = document_lock_clone .write() @@ -900,7 +904,7 @@ impl WasmIotaDocument { ) -> Result { let document_lock_clone: Rc = self.0.clone(); let jpt_claims = jpt_claims.into_serde().wasm_result()?; - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options = options.into(); let promise: Promise = future_to_promise(async move { let jwp: String = document_lock_clone @@ -948,7 +952,7 @@ impl WasmIotaDocument { custom_claims: Option, ) -> Result { let document_lock_clone: Rc = self.0.clone(); - let storage_clone: Rc = storage.0.clone(); + let storage_clone: Arc = storage.0.clone(); let options = options.into(); let custom_claims = custom_claims.and_then(|claims| claims.into_serde().ok()); let promise: Promise = future_to_promise(async move { diff --git a/bindings/wasm/src/iota/iota_document_metadata.rs b/bindings/wasm/identity_wasm/src/iota/iota_document_metadata.rs similarity index 100% rename from bindings/wasm/src/iota/iota_document_metadata.rs rename to bindings/wasm/identity_wasm/src/iota/iota_document_metadata.rs diff --git a/bindings/wasm/src/iota/iota_metadata_encoding.rs b/bindings/wasm/identity_wasm/src/iota/iota_metadata_encoding.rs similarity index 100% rename from bindings/wasm/src/iota/iota_metadata_encoding.rs rename to bindings/wasm/identity_wasm/src/iota/iota_metadata_encoding.rs diff --git a/bindings/wasm/src/iota/mod.rs b/bindings/wasm/identity_wasm/src/iota/mod.rs similarity index 100% rename from bindings/wasm/src/iota/mod.rs rename to bindings/wasm/identity_wasm/src/iota/mod.rs diff --git a/bindings/wasm/src/jose/decoded_jws.rs b/bindings/wasm/identity_wasm/src/jose/decoded_jws.rs similarity index 100% rename from bindings/wasm/src/jose/decoded_jws.rs rename to bindings/wasm/identity_wasm/src/jose/decoded_jws.rs diff --git a/bindings/wasm/src/jose/jwk.rs b/bindings/wasm/identity_wasm/src/jose/jwk.rs similarity index 100% rename from bindings/wasm/src/jose/jwk.rs rename to bindings/wasm/identity_wasm/src/jose/jwk.rs diff --git a/bindings/wasm/src/jose/jws_header.rs b/bindings/wasm/identity_wasm/src/jose/jws_header.rs similarity index 100% rename from bindings/wasm/src/jose/jws_header.rs rename to bindings/wasm/identity_wasm/src/jose/jws_header.rs diff --git a/bindings/wasm/src/jose/jwu.rs b/bindings/wasm/identity_wasm/src/jose/jwu.rs similarity index 100% rename from bindings/wasm/src/jose/jwu.rs rename to bindings/wasm/identity_wasm/src/jose/jwu.rs diff --git a/bindings/wasm/src/jose/mod.rs b/bindings/wasm/identity_wasm/src/jose/mod.rs similarity index 100% rename from bindings/wasm/src/jose/mod.rs rename to bindings/wasm/identity_wasm/src/jose/mod.rs diff --git a/bindings/wasm/src/jose/types.rs b/bindings/wasm/identity_wasm/src/jose/types.rs similarity index 100% rename from bindings/wasm/src/jose/types.rs rename to bindings/wasm/identity_wasm/src/jose/types.rs diff --git a/bindings/wasm/src/jpt/encoding.rs b/bindings/wasm/identity_wasm/src/jpt/encoding.rs similarity index 100% rename from bindings/wasm/src/jpt/encoding.rs rename to bindings/wasm/identity_wasm/src/jpt/encoding.rs diff --git a/bindings/wasm/src/jpt/issuer_protected_header.rs b/bindings/wasm/identity_wasm/src/jpt/issuer_protected_header.rs similarity index 100% rename from bindings/wasm/src/jpt/issuer_protected_header.rs rename to bindings/wasm/identity_wasm/src/jpt/issuer_protected_header.rs diff --git a/bindings/wasm/src/jpt/jpt_claims.rs b/bindings/wasm/identity_wasm/src/jpt/jpt_claims.rs similarity index 100% rename from bindings/wasm/src/jpt/jpt_claims.rs rename to bindings/wasm/identity_wasm/src/jpt/jpt_claims.rs diff --git a/bindings/wasm/src/jpt/jwp_issued.rs b/bindings/wasm/identity_wasm/src/jpt/jwp_issued.rs similarity index 100% rename from bindings/wasm/src/jpt/jwp_issued.rs rename to bindings/wasm/identity_wasm/src/jpt/jwp_issued.rs diff --git a/bindings/wasm/src/jpt/jwp_presentation_builder.rs b/bindings/wasm/identity_wasm/src/jpt/jwp_presentation_builder.rs similarity index 100% rename from bindings/wasm/src/jpt/jwp_presentation_builder.rs rename to bindings/wasm/identity_wasm/src/jpt/jwp_presentation_builder.rs diff --git a/bindings/wasm/src/jpt/mod.rs b/bindings/wasm/identity_wasm/src/jpt/mod.rs similarity index 100% rename from bindings/wasm/src/jpt/mod.rs rename to bindings/wasm/identity_wasm/src/jpt/mod.rs diff --git a/bindings/wasm/src/jpt/payload.rs b/bindings/wasm/identity_wasm/src/jpt/payload.rs similarity index 100% rename from bindings/wasm/src/jpt/payload.rs rename to bindings/wasm/identity_wasm/src/jpt/payload.rs diff --git a/bindings/wasm/src/jpt/presentation_protected_header.rs b/bindings/wasm/identity_wasm/src/jpt/presentation_protected_header.rs similarity index 100% rename from bindings/wasm/src/jpt/presentation_protected_header.rs rename to bindings/wasm/identity_wasm/src/jpt/presentation_protected_header.rs diff --git a/bindings/wasm/src/jpt/proof_algorithm.rs b/bindings/wasm/identity_wasm/src/jpt/proof_algorithm.rs similarity index 100% rename from bindings/wasm/src/jpt/proof_algorithm.rs rename to bindings/wasm/identity_wasm/src/jpt/proof_algorithm.rs diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs new file mode 100644 index 0000000000..d0be5ec0cd --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity.rs @@ -0,0 +1,152 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// This file has been moved here from identity_iota_core/src/client_dummy. +// The file will be removed after the TS-Client-SDK is integrated. +// The file provides a POC for the wasm-bindgen glue code needed to +// implement the TS-Client-SDK integration. + +use std::collections::HashMap; +use std::str::FromStr; + +use serde; +use serde::Deserialize; +use serde::Serialize; + +use super::DummySigner; +use super::IdentityClient; +use super::Multicontroller; +use super::Proposal; +use identity_iota::iota::rebased::Error; +use identity_iota::iota::IotaDocument; +use identity_iota::iota_interaction::rpc_types::IotaObjectData; +use identity_iota::iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota::iota_interaction::types::base_types::IotaAddress; +use identity_iota::iota_interaction::types::base_types::ObjectID; +use identity_iota::iota_interaction::types::id::UID; +use identity_iota::iota_interaction::IotaClientTrait; +use iota_interaction_ts::error::TsSdkError; + +#[derive(Debug, Deserialize, Serialize)] +pub struct OnChainIdentity { + pub id: UID, + pub did_doc: Multicontroller>, + #[serde(skip)] + pub obj_ref: Option, + // used to have something to return a reference for in getter test + #[serde(skip)] + pub proposals: HashMap, +} + +impl OnChainIdentity { + pub fn is_shared(&self) -> bool { + true + } + + pub fn proposals(&self) -> &HashMap { + &self.proposals + } + + pub fn update_did_document(self, updated_doc: IotaDocument) -> ProposalBuilder + where + T: IotaClientTrait, + { + ProposalBuilder::new(self, ProposalAction::UpdateDocument(updated_doc)) + } + + pub fn deactivate_did(self) -> ProposalBuilder + where + T: IotaClientTrait, + { + ProposalBuilder::new(self, ProposalAction::Deactivate) + } + + pub async fn get_history( + &self, + _client: &IdentityClient, + _last_version: Option<&IotaObjectData>, + _page_size: Option, + ) -> Result, Error> + where + T: IotaClientTrait, + { + Ok(vec![]) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum ProposalAction { + UpdateDocument(IotaDocument), + Deactivate, +} + +#[derive(Debug)] +pub struct ProposalBuilder {} + +impl ProposalBuilder { + pub fn new(_identity: OnChainIdentity, _action: ProposalAction) -> Self { + Self {} + } + + pub fn expiration_epoch(self, _exp: u64) -> Self { + self + } + + pub fn key(self, _key: String) -> Self { + self + } + + pub fn gas_budget(self, _amount: u64) -> Self { + self + } + + pub async fn finish(self, _client: &IdentityClient, _signer: &DummySigner) -> Result, Error> + where + C: IotaClientTrait, + { + Ok(Some(Proposal {})) + } +} + +#[derive(Debug)] +pub struct IdentityBuilder {} + +impl IdentityBuilder { + pub fn new(_did_doc: &[u8], _package_id: ObjectID) -> Self { + Self {} + } + + pub fn controller(self, _address: IotaAddress, _voting_power: u64) -> Self { + self + } + + pub fn threshold(self, _threshold: u64) -> Self { + self + } + + pub fn gas_budget(self, _gas_budget: u64) -> Self { + self + } + + pub fn controllers(self, _controllers: I) -> Self + where + I: IntoIterator, + { + self + } + + pub async fn finish(self, _client: &IdentityClient, _signer: &DummySigner) -> Result + where + C: IotaClientTrait, + { + Ok(OnChainIdentity { + id: UID::new( + ObjectID::from_str("did:iota:foobar:0x0000000000000000000000000000000000000000000000000000000000000000") + .map_err(|e| Error::InvalidArgument(e.to_string()))?, + ), + did_doc: Multicontroller::new(vec![1u8, 2u8, 3u8]), + obj_ref: None, + proposals: HashMap::new(), + }) + } +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client.rs new file mode 100644 index 0000000000..7e69fcb597 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client.rs @@ -0,0 +1,158 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// This file has been moved here from identity_iota_core/src/client_dummy. +// The file will be removed after the TS-Client-SDK is integrated. +// The file provides a POC for the wasm-bindgen glue code needed to +// implement the TS-Client-SDK integration. + +use std::str::FromStr; + +use identity_iota::iota::rebased::rebased_err; +use identity_iota::iota::rebased::Error; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::NetworkName; +use identity_iota::iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; +use identity_iota::iota_interaction::types::base_types::IotaAddress; +use identity_iota::iota_interaction::types::base_types::ObjectID; +use identity_iota::iota_interaction::IotaClientTrait; +use identity_iota::iota_interaction::IotaTransactionBlockResponseT; +use identity_iota::iota_interaction::SignatureBcs; +use identity_iota::iota_interaction::TransactionDataBcs; + +use super::DummySigner; +use super::Identity; +use super::IdentityBuilder; +use super::IdentityClientBuilder; +use iota_interaction_ts::bindings::IotaTransactionBlockResponseAdapter; +use iota_interaction_ts::error::TsSdkError; + +// `IdentityClient` is a dummy placeholder to prepare wasm bindings for the actual one +// as long as it is not compilable to wasm + +pub struct IdentityClient { + client: T, + network_name: NetworkName, + identity_iota_package_id: IotaAddress, + sender_address: IotaAddress, + sender_public_key: Vec, +} + +// builder related functions +impl IdentityClient +where + T: IotaClientTrait, +{ + pub fn builder() -> IdentityClientBuilder { + IdentityClientBuilder::::default() + } + + pub(crate) fn from_builder(builder: IdentityClientBuilder) -> Result { + let network_name = NetworkName::try_from(builder.network_name.unwrap_or("dummy".to_string())).unwrap(); + let sender_address = builder.sender_address.unwrap_or(IotaAddress::default()); + Ok(Self { + client: builder.iota_client.unwrap(), + network_name, + identity_iota_package_id: IotaAddress::from( + builder.identity_iota_package_id.unwrap_or( + ObjectID::from_str("0x0101010101010101010101010101010101010101010101010101010101010101") + .map_err(|e| Error::InvalidArgument(e.to_string()))?, + ), + ), + sender_public_key: builder.sender_public_key.unwrap_or(vec![1u8, 2u8, 3u8, 4u8]), + sender_address, + }) + } +} + +// mock functions for wasm integration +impl IdentityClient +where + T: IotaClientTrait, +{ + pub fn sender_public_key(&self) -> Result<&[u8], Error> { + Ok(self.sender_public_key.as_ref()) + } + + pub fn sender_address(&self) -> Result { + Ok(self.sender_address.clone()) + } + + pub fn network_name(&self) -> &NetworkName { + &self.network_name + } + + pub fn create_identity(&self, _iota_document: &[u8]) -> IdentityBuilder { + IdentityBuilder::new( + &[], + ObjectID::from_str("foobar").expect("foobar can not be parsed into ObjectId"), + ) + } + + pub async fn get_identity(&self, _object_id: ObjectID) -> Result { + unimplemented!("get_identity"); + } + + pub async fn execute_dummy_transaction( + &self, + tx_data_bcs: TransactionDataBcs, + signatures: Vec, + ) -> Result< + Box>, + Error, + > { + let tx_response = self + .client + .quorum_driver_api() + .execute_transaction_block( + &tx_data_bcs, + &signatures, + Some(IotaTransactionBlockResponseOptions::new().with_effects()), + None, + ) + .await?; + Ok(tx_response) + } + + pub async fn resolve_did(&self, _did: &IotaDID) -> Result { + unimplemented!("resolve_did"); + } + + pub async fn publish_did_document( + &self, + _document: IotaDocument, + _gas_budget: u64, + _signer: &DummySigner, + ) -> Result { + unimplemented!("publish_did_document"); + } + + pub async fn publish_did_document_update( + &self, + _document: IotaDocument, + _gas_budget: u64, + _signer: &DummySigner, + ) -> Result { + unimplemented!("publish_did_document_update"); + } + + pub async fn deactivate_did_output( + &self, + _did: &IotaDID, + _gas_budget: u64, + _signer: &DummySigner, + ) -> Result<(), Error> { + unimplemented!("deactivate_did_output"); + } +} + +// test function(s) for wasm calling test +impl IdentityClient +where + T: IotaClientTrait, +{ + pub async fn get_chain_identifier(&self) -> Result { + self.client.read_api().get_chain_identifier().await.map_err(rebased_err) + } +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client_builder.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client_builder.rs new file mode 100644 index 0000000000..6a3f1e3889 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/identity_client_builder.rs @@ -0,0 +1,82 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// This file has been moved here from identity_iota_core/src/client_dummy. +// The file will be removed after the TS-Client-SDK is integrated. +// The file provides a POC for the wasm-bindgen glue code needed to +// implement the TS-Client-SDK integration. + +use super::IdentityClient; +use identity_iota::iota::rebased::Error; +use identity_iota::iota_interaction::types::base_types::IotaAddress; +use identity_iota::iota_interaction::types::base_types::ObjectID; +use identity_iota::iota_interaction::IotaClientTrait; +use iota_interaction_ts::error::TsSdkError; + +pub struct IdentityClientBuilder { + pub(crate) identity_iota_package_id: Option, + pub(crate) sender_public_key: Option>, + pub(crate) sender_address: Option, + pub(crate) iota_client: Option, + pub(crate) network_name: Option, +} + +impl IdentityClientBuilder +where + T: IotaClientTrait, +{ + /// Sets the `identity_iota_package_id` value. + #[must_use] + pub fn identity_iota_package_id(mut self, value: ObjectID) -> Self { + self.identity_iota_package_id = Some(value); + self + } + + /// Sets the `sender_public_key` value. + #[must_use] + pub fn sender_public_key(mut self, value: &[u8]) -> Self { + self.sender_public_key = Some(value.into()); + self + } + + /// Sets the `sender_address` value. + #[must_use] + pub fn sender_address(mut self, value: &IotaAddress) -> Self { + self.sender_address = Some(value.clone()); + self + } + + /// Sets the `iota_client` value. + #[must_use] + pub fn iota_client(mut self, value: T) -> Self { + self.iota_client = Some(value); + self + } + + /// Sets the `network_name` value. + #[must_use] + pub fn network_name(mut self, value: &str) -> Self { + self.network_name = Some(value.to_string()); + self + } + + /// Returns a new `KinesisIdentityClientDummyBuilder` based on the `KinesisIdentityClientDummyBuilder` configuration. + pub fn build(self) -> Result, Error> { + IdentityClient::from_builder(self) + } +} + +impl Default for IdentityClientBuilder +where + T: IotaClientTrait, +{ + fn default() -> Self { + Self { + identity_iota_package_id: None, + sender_public_key: None, + sender_address: None, + iota_client: None, + network_name: None, + } + } +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs new file mode 100644 index 0000000000..9b66b4a40b --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/mod.rs @@ -0,0 +1,17 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod identity; +mod identity_client; +mod identity_client_builder; +mod multicontroller; + +pub use identity::*; +pub use identity_client::*; +pub use identity_client_builder::*; +pub use multicontroller::*; + +// dummy types, have to be replaced with actual types later on +pub type DummySigner = str; +pub type Hashable = Vec; +pub type Identity = (); diff --git a/bindings/wasm/identity_wasm/src/kinesis/client_dummy/multicontroller.rs b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/multicontroller.rs new file mode 100644 index 0000000000..023ca74cdc --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/client_dummy/multicontroller.rs @@ -0,0 +1,56 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// This file has been moved here from identity_iota_core/src/client_dummy. +// The file will be removed after the TS-Client-SDK is integrated. +// The file provides a POC for the wasm-bindgen glue code needed to +// implement the TS-Client-SDK integration. + +use std::collections::HashMap; + +use serde::Deserialize; +use serde::Serialize; + +use identity_iota::iota_interaction::types::base_types::ObjectID; + +use super::Hashable; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Proposal {} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Multicontroller { + controlled_value: T, + controllers: HashMap, u64>, + proposals: HashMap, +} + +impl Multicontroller { + /// TODO: remove this, added to test interface in ts + pub fn new(controlled_value: T) -> Self { + Self { + controlled_value, + controllers: HashMap::new(), + proposals: HashMap::new(), + } + } + + pub fn controlled_value(&self) -> &T { + &self.controlled_value + } + pub fn threshold(&self) -> u64 { + 123 + } + pub fn controller_voting_power(&self, _controller_cap_id: ObjectID) -> Option { + Some(123) + } + pub fn proposals(&self) -> &HashMap { + &self.proposals + } + pub fn into_inner(self) -> T { + self.controlled_value + } + pub fn has_member(&self, _cap_id: ObjectID) -> bool { + true + } +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/identity.rs b/bindings/wasm/identity_wasm/src/kinesis/identity.rs new file mode 100644 index 0000000000..08904ae4af --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/identity.rs @@ -0,0 +1,218 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::iota::IotaDocument; +use tsify::Tsify; +use wasm_bindgen::prelude::*; + +use iota_interaction_ts::bindings::WasmIotaObjectData; + +use iota_interaction_ts::iota_client_ts_sdk::IotaClientTsSdk; + +use crate::error::wasm_error; +use crate::error::Result; +use crate::iota::WasmIotaDocument; + +use super::client_dummy::DummySigner; +use super::client_dummy::IdentityBuilder; +use super::client_dummy::OnChainIdentity; +use super::client_dummy::ProposalAction; +use super::client_dummy::ProposalBuilder; +use super::types::into_sdk_type; +use super::types::WasmIotaAddress; +use super::types::WasmObjectID; +use super::WasmKinesisIdentityClient; +use super::WasmProposal; + +/// Helper type for `WasmIdentityBuilder::controllers`. +/// Has getters to support `Clone` for serialization +#[derive(Debug)] +#[wasm_bindgen(getter_with_clone)] +pub struct ControllerAndVotingPower(pub WasmIotaAddress, pub u64); + +#[wasm_bindgen(js_class = ControllerAndVotingPower)] +impl ControllerAndVotingPower { + #[wasm_bindgen(constructor)] + pub fn new(address: WasmIotaAddress, voting_power: u64) -> Self { + Self(address, voting_power) + } +} + +#[wasm_bindgen(js_name = OnChainIdentity)] +pub struct WasmOnChainIdentity(pub(crate) OnChainIdentity); + +#[wasm_bindgen(js_class = OnChainIdentity)] +impl WasmOnChainIdentity { + #[wasm_bindgen(js_name = isShared)] + pub fn is_shared(&self) -> bool { + self.0.is_shared() + } + + #[wasm_bindgen(skip_typescript)] // ts type in custom section below + pub fn proposals(&self) -> Result { + serde_wasm_bindgen::to_value(&self.0.proposals()).map_err(wasm_error) + } + + #[wasm_bindgen(js_name = updateDidDocument)] + pub fn update_did_document(self, updated_doc: WasmIotaDocument) -> Result { + let doc: IotaDocument = updated_doc + .0 + .try_read() + .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? + .clone(); + Ok(WasmProposalBuilder(self.0.update_did_document::(doc))) + } + + #[wasm_bindgen(js_name = deactivateDid)] + pub fn deactivate_did(self) -> WasmProposalBuilder { + WasmProposalBuilder(self.0.deactivate_did::()) + } + + #[wasm_bindgen(js_name = getHistory, skip_typescript)] // ts type in custom section below + pub async fn get_history( + &self, + client: WasmKinesisIdentityClient, + last_version: Option, + page_size: Option, + ) -> Result { + let rs_history = self + .0 + .get_history( + &client.0, + last_version.map(|lv| into_sdk_type(lv).unwrap()).as_ref(), + page_size, + ) + .await + .map_err(wasm_error)?; + serde_wasm_bindgen::to_value(&rs_history).map_err(wasm_error) + } +} + +// Manually add the method to the interface. +#[wasm_bindgen(typescript_custom_section)] +const WASM_ON_CHAIN_IDENTITY_TYPES: &str = r###" + export interface OnChainIdentity { + proposals(): Map; + getHistory(): Map; + } +"###; + +#[derive(Tsify, Serialize, Deserialize)] +#[tsify(into_wasm_abi, from_wasm_abi)] +#[serde(rename = "ProposalAction")] +pub enum WasmProposalAction { + UpdateDocument(IotaDocument), + Deactivate, +} + +impl From for ProposalAction { + fn from(val: WasmProposalAction) -> Self { + match val { + WasmProposalAction::UpdateDocument(doc) => ProposalAction::UpdateDocument(doc), + WasmProposalAction::Deactivate => ProposalAction::Deactivate, + } + } +} + +// TODO: remove the following comment and commented out code if we don't run into a rename issue +// -> in case `serde(rename` runs into issues with properties with renamed types still having the +// original type, see [here](https://github.com/madonoharu/tsify/issues/43) for an example +// #[declare] +// pub type ProposalAction = WasmProposalAction; + +#[wasm_bindgen(js_name = ProposalBuilder)] +pub struct WasmProposalBuilder(pub(crate) ProposalBuilder); + +#[wasm_bindgen(js_class = ProposalBuilder)] +impl WasmProposalBuilder { + #[wasm_bindgen(constructor)] + pub fn new(identity: WasmOnChainIdentity, action: WasmProposalAction) -> Self { + Self(ProposalBuilder::new(identity.0, action.into())) + } + + #[wasm_bindgen(js_name = expirationEpoch)] + pub fn expiration_epoch(self, exp: u64) -> Self { + Self(self.0.expiration_epoch(exp)) + } + + pub fn key(self, key: String) -> Self { + Self(self.0.key(key)) + } + + #[wasm_bindgen(js_name = gasBudget)] + pub fn gas_budget(self, amount: u64) -> Self { + Self(self.0.gas_budget(amount)) + } + + pub async fn finish(self, client: &WasmKinesisIdentityClient, signer: &DummySigner) -> Result> { + self + .0 + .finish(&client.0, signer) + .await + .map(|option| option.map(WasmProposal)) + .map_err(wasm_error) + } +} + +#[wasm_bindgen(js_name = IdentityBuilder)] +pub struct WasmIdentityBuilder(pub(crate) IdentityBuilder); + +#[wasm_bindgen(js_class = IdentityBuilder)] +impl WasmIdentityBuilder { + #[wasm_bindgen(constructor)] + pub fn new(did_doc: &[u8], _package_id: WasmObjectID) -> Self { + Self(IdentityBuilder::new( + did_doc, + "foobar" + .parse() + .expect("foobar won't be parsable into an ObjectID, TODO: Replace foobar with correct ObjectID"), + )) + } + + pub fn controller(self, address: WasmIotaAddress, voting_power: u64) -> Self { + Self( + self.0.controller( + address + .parse() + .expect("Parameter address could not be parsed into valid IotaAddress"), + voting_power, + ), + ) + } + + pub fn threshold(self, threshold: u64) -> Self { + Self(self.0.threshold(threshold)) + } + + #[wasm_bindgen(js_name = gasBudget)] + pub fn gas_budget(self, gas_budget: u64) -> Self { + Self(self.0.gas_budget(gas_budget)) + } + + pub fn controllers(self, controllers: Vec) -> Self { + Self( + self.0.controllers( + controllers + .into_iter() + .map(|v| { + ( + v.0 + .parse() + .expect("controller can not be parsed into valid IotaAddress"), + v.1, + ) + }) + .collect::>(), + ), + ) + } + + pub async fn finish(self, client: &WasmKinesisIdentityClient, signer: &DummySigner) -> Result { + self + .0 + .finish(&client.0, signer) + .await + .map(WasmOnChainIdentity) + .map_err(wasm_error) + } +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/mod.rs b/bindings/wasm/identity_wasm/src/kinesis/mod.rs new file mode 100644 index 0000000000..6d00540c6b --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/mod.rs @@ -0,0 +1,15 @@ +// Copyright 2020-2022 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod client_dummy; +mod identity; +mod multicontroller; +mod types; +mod wasm_identity_client; +mod wasm_identity_client_builder; + +pub use identity::*; +pub use multicontroller::*; +pub use types::*; +pub use wasm_identity_client::*; +pub use wasm_identity_client_builder::*; diff --git a/bindings/wasm/identity_wasm/src/kinesis/multicontroller.rs b/bindings/wasm/identity_wasm/src/kinesis/multicontroller.rs new file mode 100644 index 0000000000..a74a290103 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/multicontroller.rs @@ -0,0 +1,76 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota::core::Object; + +use super::client_dummy::Multicontroller; +use super::client_dummy::Proposal; +use wasm_bindgen::prelude::*; + +use crate::common::MapStringAny; + +use super::types::WasmObjectID; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[wasm_bindgen(js_name = Proposal)] +pub struct WasmProposal(pub(crate) Proposal); + +#[wasm_bindgen(js_name = Multicontroller)] +pub struct WasmMulticontroller(pub(crate) Multicontroller>); + +#[wasm_bindgen(js_class = Multicontroller)] +impl WasmMulticontroller { + /// TODO: remove this, added to test interface in ts + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + Self(Multicontroller::>::new(vec![])) + } + + #[wasm_bindgen(js_name = controlledValue)] + pub fn controlled_value(&self) -> Vec { + self.0.controlled_value().clone() + } + + pub fn threshold(&self) -> u64 { + self.0.threshold() + } + + #[wasm_bindgen(js_name = controllerVotingPower)] + pub fn controller_voting_power(&self, controller_cap_id: WasmObjectID) -> Option { + self.0.controller_voting_power( + controller_cap_id + .parse() + .expect("Could not parse controller_cap_id into ObjectID"), + ) + } + + pub fn proposals(&self) -> Result { + let object_result: Result = self + .0 + .proposals() + .iter() + .map(|(k, v)| { + serde_json::to_value(v) + .map(|json_value| (k.to_owned(), json_value)) + .map_err(|err| JsValue::from_str(&format!("failed to serialize value; {err}"))) + }) + .collect(); + + MapStringAny::try_from(object_result?) + } + + /// Behaves as as alias for `controlled_value`, as TypeScript does not reflect consuming `self` + /// very well. Using the `Multicontroller` afterwards instance would not produce a compiler error + /// but would return a "null pointer passed to rust" error. + /// + /// TODO: consider removing this function from the bindings + #[wasm_bindgen(js_name = intoInner)] + pub fn into_inner(&self) -> Vec { + self.controlled_value() + } + + #[wasm_bindgen(js_name = hasMember)] + pub fn has_member(&self, cap_id: WasmObjectID) -> bool { + self.0.has_member(cap_id.parse().expect("cap_id is no valid ObjectID")) + } +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/types.rs b/bindings/wasm/identity_wasm/src/kinesis/types.rs new file mode 100644 index 0000000000..7ef4ac6e97 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/types.rs @@ -0,0 +1,37 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::de::DeserializeOwned; +use wasm_bindgen::prelude::*; + +use crate::error::wasm_error; +use crate::error::Result; + +pub use iota_interaction_ts::bindings::*; + +pub fn into_sdk_type>(wasm_type_instance: W) -> Result { + let js_value: JsValue = wasm_type_instance.into(); + //serde_wasm_bindgen::from_value::(js_value).map_err(wasm_error) + match serde_wasm_bindgen::from_value::(js_value.clone()) { + Ok(ret_val) => Ok(ret_val), + Err(e) => { + // TODO: Replace all console_log! usages by proper Error management and Result types. + // Use console_log! only for debug purposes + console_log!( + "[identity_wasm::kinesis::types - fn into_sdk_type]\n js_value: {:?}\n Error: {:?}", + js_value, + e + ); + Err(wasm_error(e)) + } + } +} + +pub(crate) type WasmIotaAddress = String; +pub(crate) type WasmObjectID = String; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseBalance; +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs new file mode 100644 index 0000000000..8ff6b98dbc --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client.rs @@ -0,0 +1,216 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::rc::Rc; + +use fastcrypto::ed25519::Ed25519PublicKey; +use fastcrypto::traits::ToFromBytes; + +use identity_iota::iota_interaction::types::base_types::IotaAddress; +use identity_iota::iota_interaction::SignatureBcs; + +use identity_iota::core::Base; +use identity_iota::core::BaseEncoding; + +use iota_interaction_ts::bindings::WasmExecutionStatus; +use iota_interaction_ts::bindings::WasmOwnedObjectRef; +use iota_interaction_ts::iota_client_ts_sdk::IotaClientTsSdk; + +use identity_iota::iota::rebased::Error; + +use super::client_dummy::DummySigner; +use super::client_dummy::Identity; +use super::client_dummy::IdentityClient; + +use crate::iota::IotaDocumentLock; +use crate::iota::WasmIotaDID; +use crate::iota::WasmIotaDocument; +use identity_iota::iota::IotaDocument; +use wasm_bindgen::prelude::*; + +use super::types::WasmIotaAddress; +use super::types::WasmObjectID; +use super::wasm_identity_client_builder::WasmKinesisIdentityClientBuilder; +use super::WasmIdentityBuilder; + +#[wasm_bindgen(getter_with_clone, inspectable, js_name = IotaTransactionBlockResponseEssence)] +pub struct WasmIotaTransactionBlockResponseEssence { + #[wasm_bindgen(js_name = effectsExist)] + pub effects_exist: bool, + pub effects: String, + #[wasm_bindgen(js_name = effectsExecutionStatus)] + pub effects_execution_status: Option, + #[wasm_bindgen(js_name = effectsCreated)] + pub effects_created: Option>, +} + +#[wasm_bindgen(js_name = KinesisIdentityClient)] +pub struct WasmKinesisIdentityClient(pub(crate) IdentityClient); + +// builder related functions +#[wasm_bindgen(js_class = KinesisIdentityClient)] +impl WasmKinesisIdentityClient { + #[wasm_bindgen] + pub fn builder() -> WasmKinesisIdentityClientBuilder { + // WasmKinesisIdentityClientBuilder::default() + WasmKinesisIdentityClientBuilder(IdentityClient::::builder()) + } + + // mock functions for wasm integration + + #[wasm_bindgen(js_name = senderPublicKey)] + pub fn sender_public_key(&self) -> Result, JsError> { + self.0.sender_public_key().map(|v| v.to_vec()).map_err(|e| e.into()) + } + + #[wasm_bindgen(js_name = senderAddress)] + pub fn sender_address(&self) -> Result { + self.0.sender_address().map(|a| a.to_string()).map_err(|e| e.into()) + } + + #[wasm_bindgen(js_name = networkName)] + pub fn network_name(&self) -> String { + self.0.network_name().to_string() + } + + #[wasm_bindgen(js_name = createIdentity)] + pub fn create_identity(&self, iota_document: &[u8]) -> WasmIdentityBuilder { + WasmIdentityBuilder(self.0.create_identity(iota_document)) + } + + #[wasm_bindgen(js_name = getIdentity)] + pub async fn get_identity(&self, object_id: WasmObjectID) -> Result { + self.0.get_identity(object_id.parse()?).await.map_err(|e| e.into()) + } + + #[wasm_bindgen(js_name = executeDummyTransaction)] + pub async fn execute_dummy_transaction( + &self, + tx_data_bcs_str: String, + signatures_str: Vec, + ) -> Result { + let dummy = 1; + let tx_data_bcs = BaseEncoding::decode(tx_data_bcs_str.as_str(), Base::Base64Pad)?; + let signatures = signatures_str + .iter() + .map(|s| BaseEncoding::decode(s, Base::Base64Pad)) + .collect::, _>>()?; + + let response = self + .0 + .execute_dummy_transaction(tx_data_bcs, signatures) + .await + .map_err(>::into)?; + + let effects_execution_status: Option = response + .effects_execution_status() + .map(|status| serde_wasm_bindgen::to_value(&status).unwrap().into()); + + let effects_created: Option> = response.effects_created().map(|effects| { + effects + .into_iter() + .map(|efct| serde_wasm_bindgen::to_value(&efct).unwrap().into()) + .collect() + }); + + Ok(WasmIotaTransactionBlockResponseEssence { + effects_exist: response.effects_is_some(), + effects: response.to_string(), + effects_execution_status, + effects_created, + }) + } + + #[wasm_bindgen(js_name = resolveDid)] + pub async fn resolve_did(&self, did: &WasmIotaDID) -> Result { + let document = self + .0 + .resolve_did(&did.0) + .await + .map_err(>::into)?; + Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) + } + + #[wasm_bindgen(js_name = publishDidDocument)] + pub async fn publish_did_document( + &self, + document: &WasmIotaDocument, + gas_budget: u64, + signer: &DummySigner, + ) -> Result { + let doc: IotaDocument = document + .0 + .try_read() + .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? + .clone(); + let document = self + .0 + .publish_did_document(doc, gas_budget, signer) + .await + .map_err(>::into)?; + + Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) + } + + #[wasm_bindgen(js_name = publishDidDocumentUpdate)] + pub async fn publish_did_document_update( + &self, + document: &WasmIotaDocument, + gas_budget: u64, + signer: &DummySigner, + ) -> Result { + let doc: IotaDocument = document + .0 + .try_read() + .map_err(|err| JsError::new(&format!("failed to read DID document; {err:?}")))? + .clone(); + let document = self + .0 + .publish_did_document_update(doc, gas_budget, signer) + .await + .map_err(>::into)?; + + Ok(WasmIotaDocument(Rc::new(IotaDocumentLock::new(document)))) + } + + #[wasm_bindgen(js_name = deactivateDidOutput)] + pub async fn deactivate_did_output( + &self, + did: &WasmIotaDID, + gas_budget: u64, + signer: &DummySigner, + ) -> Result<(), JsError> { + self + .0 + .deactivate_did_output(&did.0, gas_budget, signer) + .await + .map_err(>::into)?; + + Ok(()) + } + + // test function(s) for wasm calling test + + // make test call + #[wasm_bindgen(js_name = getChainIdentifier)] + pub async fn get_chain_identifier(&self) -> Result { + IdentityClient::get_chain_identifier(&self.0) + .await + .map_err(|err| JsError::new(&format!("could not get balance; {err}"))) + } +} + +/// TODO: consider importing function from rebased later on, if possible +pub fn convert_to_address(sender_public_key: &[u8]) -> Result { + let public_key = Ed25519PublicKey::from_bytes(sender_public_key) + .map_err(|err| Error::InvalidKey(format!("could not parse public key to Ed25519 public key; {err}")))?; + + Ok(IotaAddress::from(&public_key)) +} + +#[wasm_bindgen(js_name = convertToAddress)] +pub fn wasm_convert_to_address(sender_public_key: &[u8]) -> Result { + convert_to_address(sender_public_key) + .map(|v| v.to_string()) + .map_err(|err| JsError::new(&format!("could not derive address from public key; {err}"))) +} diff --git a/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_builder.rs b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_builder.rs new file mode 100644 index 0000000000..c799a4b10e --- /dev/null +++ b/bindings/wasm/identity_wasm/src/kinesis/wasm_identity_client_builder.rs @@ -0,0 +1,69 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use wasm_bindgen::prelude::*; + +use identity_iota::iota_interaction::types::base_types::IotaAddress; +use iota_interaction_ts::bindings::WasmIotaClient; +use iota_interaction_ts::iota_client_ts_sdk::IotaClientTsSdk; + +use super::client_dummy::IdentityClientBuilder; + +use crate::error::wasm_error; +use crate::error::Result; + +use super::types::WasmObjectID; +use super::WasmIotaAddress; +use super::WasmKinesisIdentityClient; + +#[derive(Default)] +#[wasm_bindgen(js_name = KinesisIdentityClientBuilder)] +pub struct WasmKinesisIdentityClientBuilder(pub(crate) IdentityClientBuilder); + +#[wasm_bindgen(js_class = KinesisIdentityClientBuilder)] +impl WasmKinesisIdentityClientBuilder { + #[wasm_bindgen(js_name = identityIotaPackageId)] + pub fn identity_iota_package_id(self, value: WasmObjectID) -> Self { + Self( + self.0.identity_iota_package_id( + value + .parse() + .expect("failed to parse identity_iota_package_id value into ObjectID"), + ), + ) + } + + #[wasm_bindgen(js_name = senderPublicKey)] + pub fn sender_public_key(self, value: &[u8]) -> Self { + Self(self.0.sender_public_key(value)) + } + + #[wasm_bindgen(js_name = senderAddress)] + pub fn sender_address(self, value: WasmIotaAddress) -> Self { + Self( + self + .0 + .sender_address(&IotaAddress::from_str(&value).expect("failed to parse sender_address value into IotaAddress")), + ) + } + + #[wasm_bindgen(js_name = iotaClient)] + pub fn iota_client(self, value: WasmIotaClient) -> Self { + Self( + self + .0 + .iota_client(IotaClientTsSdk::new(value).expect("IotaClientTsSdk could not be initialized")), + ) + } + + #[wasm_bindgen(js_name = networkName)] + pub fn network_name(self, value: &str) -> Self { + Self(self.0.network_name(value)) + } + + pub fn build(self) -> Result { + Ok(WasmKinesisIdentityClient(self.0.build().map_err(wasm_error)?)) + } +} diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/identity_wasm/src/lib.rs similarity index 77% rename from bindings/wasm/src/lib.rs rename to bindings/wasm/identity_wasm/src/lib.rs index cf8344925a..2373fb9819 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/identity_wasm/src/lib.rs @@ -31,6 +31,14 @@ pub mod sd_jwt; pub mod storage; pub mod verification; +#[cfg(feature = "dummy-client")] +// Currently it's unclear if this module will be removed or can be used for integration or unit tests. +// TODO manage the final location of the kinesis TS module +pub(crate) mod kinesis; +#[cfg(feature = "dummy-client")] +// Remove this module when working on [Issue #1445 Replace mocked Identity client with real Identity client] +pub(crate) mod obsolete; + /// Initializes the console error panic hook for better error messages #[wasm_bindgen(start)] pub fn start() -> Result<(), JsValue> { diff --git a/bindings/wasm/src/macros.rs b/bindings/wasm/identity_wasm/src/macros.rs similarity index 74% rename from bindings/wasm/src/macros.rs rename to bindings/wasm/identity_wasm/src/macros.rs index ecc99a8082..5aae9b3912 100644 --- a/bindings/wasm/src/macros.rs +++ b/bindings/wasm/identity_wasm/src/macros.rs @@ -1,6 +1,8 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use wasm_bindgen::prelude::wasm_bindgen; + #[macro_export] macro_rules! log { ($($tt:tt)*) => { @@ -8,6 +10,21 @@ macro_rules! log { } } +/// Log to console utility without the need for web_sys dependency +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console, js_name = log)] + pub fn console_log(s: &str); +} + +/// Logging macro without the need for web_sys dependency +#[macro_export] +macro_rules! console_log { + ($($tt:tt)*) => { + crate::macros::console_log((format!($($tt)*)).as_str()) + } +} + #[macro_export] macro_rules! impl_wasm_clone { ($wasm_class:ident, $js_class:ident) => { diff --git a/bindings/wasm/identity_wasm/src/obsolete/identity_client.rs b/bindings/wasm/identity_wasm/src/obsolete/identity_client.rs new file mode 100644 index 0000000000..5a442c81f4 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/obsolete/identity_client.rs @@ -0,0 +1,219 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature = "test")] +use iota_sdk::client::Client; + +use identity_iota::iota::Error; +use identity_iota::iota::IotaDID; +use identity_iota::iota::IotaDocument; +use identity_iota::iota::NetworkName; +use identity_iota::iota::Result; +use iota_sdk::types::block::address::Address; +use iota_sdk::types::block::output::feature::SenderFeature; +use iota_sdk::types::block::output::unlock_condition::GovernorAddressUnlockCondition; +use iota_sdk::types::block::output::unlock_condition::StateControllerAddressUnlockCondition; +use iota_sdk::types::block::output::AliasId; +use iota_sdk::types::block::output::AliasOutput; +use iota_sdk::types::block::output::AliasOutputBuilder; +use iota_sdk::types::block::output::Feature; +use iota_sdk::types::block::output::OutputId; +use iota_sdk::types::block::output::RentStructure; +use iota_sdk::types::block::output::UnlockCondition; +use iota_sdk::types::block::protocol::ProtocolParameters; + +/// Helper functions necessary for the [`IotaIdentityClientExt`] trait. +#[async_trait::async_trait(?Send)] +pub trait IotaIdentityClient { + /// Resolve an Alias identifier, returning its latest [`OutputId`] and [`AliasOutput`]. + async fn get_alias_output(&self, alias_id: AliasId) -> Result<(OutputId, AliasOutput)>; + /// Get the protocol parameters of the node we are trying to connect to. + async fn get_protocol_parameters(&self) -> Result; +} + +/// An extension trait that provides helper functions for publication +/// and resolution of DID documents in Alias Outputs. +/// +/// This trait is not intended to be implemented directly, a blanket implementation is +/// provided for [`IotaIdentityClient`] implementers. +#[async_trait::async_trait(?Send)] +pub trait IotaIdentityClientExt: IotaIdentityClient { + /// Create a DID with a new Alias Output containing the given `document`. + /// + /// The `address` will be set as the state controller and governor unlock conditions. + /// The minimum required token deposit amount will be set according to the given + /// `rent_structure`, which will be fetched from the node if not provided. + /// The returned Alias Output can be further customised before publication, if desired. + /// + /// NOTE: This does *not* publish the Alias Output. + /// + /// # Errors + /// + /// - [`Error::DIDUpdateError`] when retrieving the `RentStructure` fails. + /// - [`Error::AliasOutputBuildError`] when building the Alias Output fails. + async fn new_did_output( + &self, + address: Address, + document: IotaDocument, + rent_structure: Option, + ) -> Result { + let rent_structure: RentStructure = if let Some(rent) = rent_structure { + rent + } else { + self.get_rent_structure().await? + }; + + AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AliasId::null()) + .with_state_index(0) + .with_foundry_counter(0) + .with_state_metadata(document.pack()?) + .add_feature(Feature::Sender(SenderFeature::new(address))) + .add_unlock_condition(UnlockCondition::StateControllerAddress( + StateControllerAddressUnlockCondition::new(address), + )) + .add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new( + address, + ))) + .finish() + .map_err(|e| Error::DIDResolutionError(e.to_string())) + } + + /// Fetches the associated Alias Output and updates it with `document` in its state metadata. + /// The storage deposit on the output is left unchanged. If the size of the document increased, + /// the amount should be increased manually. + /// + /// NOTE: This does *not* publish the updated Alias Output. + /// + /// # Errors + /// + /// Returns `Err` when failing to resolve the DID contained in `document`. + async fn update_did_output(&self, document: IotaDocument) -> Result { + // let id: AliasId = AliasId::from(document.id()); + // let (_, alias_output) = self.get_alias_output(id).await?; + // + // let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) + // .with_state_index(alias_output.state_index() + 1) + // .with_state_metadata(document.pack()?); + // + // if alias_output.alias_id().is_null() { + // alias_output_builder = alias_output_builder.with_alias_id(id); + // } + // + // alias_output_builder.finish().map_err(|e| Error::DIDResolutionError(e.to_string())) + unimplemented!(); + } + + /// Removes the DID document from the state metadata of its Alias Output, + /// effectively deactivating it. The storage deposit on the output is left unchanged, + /// and should be reallocated manually. + /// + /// Deactivating does not destroy the output. Hence, it can be re-activated by publishing + /// an update containing a DID document. + /// + /// NOTE: this does *not* publish the updated Alias Output. + /// + /// # Errors + /// + /// Returns `Err` when failing to resolve the `did`. + async fn deactivate_did_output(&self, did: &IotaDID) -> Result { + // let alias_id: AliasId = AliasId::from(did); + // let (_, alias_output) = self.get_alias_output(alias_id).await?; + // + // let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output) + // .with_state_index(alias_output.state_index() + 1) + // .with_state_metadata(Vec::new()); + // + // if alias_output.alias_id().is_null() { + // alias_output_builder = alias_output_builder.with_alias_id(alias_id); + // } + // + // alias_output_builder.finish().map_err(|e| Error::DIDResolutionError(e.to_string())) + unimplemented!(); + } + + /// Resolve a [`IotaDocument`]. Returns an empty, deactivated document if the state metadata + /// of the Alias Output is empty. + /// + /// # Errors + /// + /// - [`NetworkMismatch`](Error::NetworkMismatch) if the network of the DID and client differ. + /// - [`NotFound`](iota_sdk::client::Error::NoOutput) if the associated Alias Output was not found. + async fn resolve_did(&self, did: &IotaDID) -> Result { + // validate_network(self, did).await?; + // + // let id: AliasId = AliasId::from(did); + // let (_, alias_output) = self.get_alias_output(id).await?; + // IotaDocument::unpack_from_output(did, &alias_output, true) + unimplemented!(); + } + + /// Fetches the [`AliasOutput`] associated with the given DID. + /// + /// # Errors + /// + /// - [`NetworkMismatch`](Error::NetworkMismatch) if the network of the DID and client differ. + /// - [`NotFound`](iota_sdk::client::Error::NoOutput) if the associated Alias Output was not found. + async fn resolve_did_output(&self, did: &IotaDID) -> Result { + // validate_network(self, did).await?; + // + // let id: AliasId = AliasId::from(did); + // self.get_alias_output(id).await.map(|(_, alias_output)| alias_output) + unimplemented!(); + } + + /// Returns the network name of the client, which is the + /// Bech32 human-readable part (HRP) of the network. + /// + /// E.g. "iota", "atoi", "smr", "rms". + async fn network_name(&self) -> Result { + self.get_network_hrp().await.and_then(NetworkName::try_from) + } + + /// Return the rent structure of the network, indicating the byte costs for outputs. + async fn get_rent_structure(&self) -> Result { + self + .get_protocol_parameters() + .await + .map(|parameters| *parameters.rent_structure()) + } + + /// Gets the token supply of the node we're connecting to. + async fn get_token_supply(&self) -> Result { + self + .get_protocol_parameters() + .await + .map(|parameters| parameters.token_supply()) + } + + /// Return the Bech32 human-readable part (HRP) of the network. + /// + /// E.g. "iota", "atoi", "smr", "rms". + async fn get_network_hrp(&self) -> Result { + self + .get_protocol_parameters() + .await + .map(|parameters| parameters.bech32_hrp().to_string()) + } +} + +#[cfg(not(feature = "test"))] +impl IotaIdentityClientExt for T where T: IotaIdentityClient {} +#[cfg(feature = "test")] +impl IotaIdentityClientExt for Client {} + +pub(super) async fn validate_network(client: &T, did: &IotaDID) -> Result<()> +where + T: IotaIdentityClient + ?Sized, +{ + let network_hrp: String = client + .get_protocol_parameters() + .await + .map(|parameters| parameters.bech32_hrp().to_string())?; + if did.network_str() != network_hrp.as_str() { + return Err(Error::NetworkMismatch { + expected: did.network_str().to_owned(), + actual: network_hrp, + }); + }; + Ok(()) +} diff --git a/bindings/wasm/identity_wasm/src/obsolete/mod.rs b/bindings/wasm/identity_wasm/src/obsolete/mod.rs new file mode 100644 index 0000000000..cef8d4bc70 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/obsolete/mod.rs @@ -0,0 +1,7 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub(crate) mod identity_client; + +pub(crate) use identity_client::IotaIdentityClient; +pub(crate) use identity_client::IotaIdentityClientExt; diff --git a/bindings/wasm/src/resolver/mod.rs b/bindings/wasm/identity_wasm/src/resolver/mod.rs similarity index 100% rename from bindings/wasm/src/resolver/mod.rs rename to bindings/wasm/identity_wasm/src/resolver/mod.rs diff --git a/bindings/wasm/src/resolver/resolver_config.rs b/bindings/wasm/identity_wasm/src/resolver/resolver_config.rs similarity index 99% rename from bindings/wasm/src/resolver/resolver_config.rs rename to bindings/wasm/identity_wasm/src/resolver/resolver_config.rs index a5ff9a74de..aba402d8c7 100644 --- a/bindings/wasm/src/resolver/resolver_config.rs +++ b/bindings/wasm/identity_wasm/src/resolver/resolver_config.rs @@ -18,7 +18,6 @@ extern "C" { #[wasm_bindgen(method, getter)] pub(crate) fn handlers(this: &ResolverConfig) -> Option; - } // Workaround because JSDocs does not support arrows (=>) while TS does not support the "function" word in type diff --git a/bindings/wasm/src/resolver/resolver_types.rs b/bindings/wasm/identity_wasm/src/resolver/resolver_types.rs similarity index 100% rename from bindings/wasm/src/resolver/resolver_types.rs rename to bindings/wasm/identity_wasm/src/resolver/resolver_types.rs diff --git a/bindings/wasm/src/resolver/wasm_resolver.rs b/bindings/wasm/identity_wasm/src/resolver/wasm_resolver.rs similarity index 99% rename from bindings/wasm/src/resolver/wasm_resolver.rs rename to bindings/wasm/identity_wasm/src/resolver/wasm_resolver.rs index ff0782b2f2..da680e0877 100644 --- a/bindings/wasm/src/resolver/wasm_resolver.rs +++ b/bindings/wasm/identity_wasm/src/resolver/wasm_resolver.rs @@ -7,7 +7,6 @@ use std::rc::Rc; use identity_iota::did::CoreDID; use identity_iota::did::DID; use identity_iota::iota::IotaDID; -use identity_iota::iota::IotaIdentityClientExt; use identity_iota::resolver::SingleThreadedResolver; use js_sys::Array; use js_sys::Function; @@ -23,6 +22,7 @@ use crate::iota::IotaDocumentLock; use crate::iota::WasmIotaDID; use crate::iota::WasmIotaDocument; use crate::iota::WasmIotaIdentityClient; +use crate::obsolete::IotaIdentityClientExt; use crate::resolver::resolver_config::MapResolutionHandler; use crate::resolver::resolver_config::ResolverConfig; use crate::resolver::PromiseArrayIToCoreDocument; diff --git a/bindings/wasm/src/revocation/bitmap.rs b/bindings/wasm/identity_wasm/src/revocation/bitmap.rs similarity index 100% rename from bindings/wasm/src/revocation/bitmap.rs rename to bindings/wasm/identity_wasm/src/revocation/bitmap.rs diff --git a/bindings/wasm/src/revocation/mod.rs b/bindings/wasm/identity_wasm/src/revocation/mod.rs similarity index 100% rename from bindings/wasm/src/revocation/mod.rs rename to bindings/wasm/identity_wasm/src/revocation/mod.rs diff --git a/bindings/wasm/src/sd_jwt/decoder.rs b/bindings/wasm/identity_wasm/src/sd_jwt/decoder.rs similarity index 100% rename from bindings/wasm/src/sd_jwt/decoder.rs rename to bindings/wasm/identity_wasm/src/sd_jwt/decoder.rs diff --git a/bindings/wasm/src/sd_jwt/disclosure.rs b/bindings/wasm/identity_wasm/src/sd_jwt/disclosure.rs similarity index 100% rename from bindings/wasm/src/sd_jwt/disclosure.rs rename to bindings/wasm/identity_wasm/src/sd_jwt/disclosure.rs diff --git a/bindings/wasm/src/sd_jwt/encoder.rs b/bindings/wasm/identity_wasm/src/sd_jwt/encoder.rs similarity index 100% rename from bindings/wasm/src/sd_jwt/encoder.rs rename to bindings/wasm/identity_wasm/src/sd_jwt/encoder.rs diff --git a/bindings/wasm/src/sd_jwt/key_binding_jwt_claims.rs b/bindings/wasm/identity_wasm/src/sd_jwt/key_binding_jwt_claims.rs similarity index 100% rename from bindings/wasm/src/sd_jwt/key_binding_jwt_claims.rs rename to bindings/wasm/identity_wasm/src/sd_jwt/key_binding_jwt_claims.rs diff --git a/bindings/wasm/src/sd_jwt/mod.rs b/bindings/wasm/identity_wasm/src/sd_jwt/mod.rs similarity index 100% rename from bindings/wasm/src/sd_jwt/mod.rs rename to bindings/wasm/identity_wasm/src/sd_jwt/mod.rs diff --git a/bindings/wasm/src/sd_jwt/wasm_sd_jwt.rs b/bindings/wasm/identity_wasm/src/sd_jwt/wasm_sd_jwt.rs similarity index 100% rename from bindings/wasm/src/sd_jwt/wasm_sd_jwt.rs rename to bindings/wasm/identity_wasm/src/sd_jwt/wasm_sd_jwt.rs diff --git a/bindings/wasm/src/storage/jpt_timeframe_revocation_ext.rs b/bindings/wasm/identity_wasm/src/storage/jpt_timeframe_revocation_ext.rs similarity index 100% rename from bindings/wasm/src/storage/jpt_timeframe_revocation_ext.rs rename to bindings/wasm/identity_wasm/src/storage/jpt_timeframe_revocation_ext.rs diff --git a/bindings/wasm/src/storage/jwk_gen_output.rs b/bindings/wasm/identity_wasm/src/storage/jwk_gen_output.rs similarity index 100% rename from bindings/wasm/src/storage/jwk_gen_output.rs rename to bindings/wasm/identity_wasm/src/storage/jwk_gen_output.rs diff --git a/bindings/wasm/src/storage/jwk_storage.rs b/bindings/wasm/identity_wasm/src/storage/jwk_storage.rs similarity index 100% rename from bindings/wasm/src/storage/jwk_storage.rs rename to bindings/wasm/identity_wasm/src/storage/jwk_storage.rs diff --git a/bindings/wasm/src/storage/jwk_storage_bbs_plus_ext.rs b/bindings/wasm/identity_wasm/src/storage/jwk_storage_bbs_plus_ext.rs similarity index 100% rename from bindings/wasm/src/storage/jwk_storage_bbs_plus_ext.rs rename to bindings/wasm/identity_wasm/src/storage/jwk_storage_bbs_plus_ext.rs diff --git a/bindings/wasm/src/storage/jwt_presentation_options.rs b/bindings/wasm/identity_wasm/src/storage/jwt_presentation_options.rs similarity index 100% rename from bindings/wasm/src/storage/jwt_presentation_options.rs rename to bindings/wasm/identity_wasm/src/storage/jwt_presentation_options.rs diff --git a/bindings/wasm/src/storage/key_id_storage.rs b/bindings/wasm/identity_wasm/src/storage/key_id_storage.rs similarity index 100% rename from bindings/wasm/src/storage/key_id_storage.rs rename to bindings/wasm/identity_wasm/src/storage/key_id_storage.rs diff --git a/bindings/wasm/src/storage/method_digest.rs b/bindings/wasm/identity_wasm/src/storage/method_digest.rs similarity index 100% rename from bindings/wasm/src/storage/method_digest.rs rename to bindings/wasm/identity_wasm/src/storage/method_digest.rs diff --git a/bindings/wasm/src/storage/mod.rs b/bindings/wasm/identity_wasm/src/storage/mod.rs similarity index 65% rename from bindings/wasm/src/storage/mod.rs rename to bindings/wasm/identity_wasm/src/storage/mod.rs index fe54110e9d..edd502e3da 100644 --- a/bindings/wasm/src/storage/mod.rs +++ b/bindings/wasm/identity_wasm/src/storage/mod.rs @@ -10,6 +10,8 @@ mod key_id_storage; mod method_digest; mod signature_options; mod wasm_storage; +// Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] +// mod wasm_storage_signer; pub use jpt_timeframe_revocation_ext::*; pub use jwk_gen_output::*; @@ -19,3 +21,5 @@ pub use key_id_storage::*; pub use method_digest::*; pub use signature_options::*; pub use wasm_storage::*; +// Uncomment this code when working on [Issue #1445 Replace mocked Identity client with real Identity client] +// pub use wasm_storage_signer::*; diff --git a/bindings/wasm/src/storage/signature_options.rs b/bindings/wasm/identity_wasm/src/storage/signature_options.rs similarity index 100% rename from bindings/wasm/src/storage/signature_options.rs rename to bindings/wasm/identity_wasm/src/storage/signature_options.rs diff --git a/bindings/wasm/src/storage/wasm_storage.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage.rs similarity index 88% rename from bindings/wasm/src/storage/wasm_storage.rs rename to bindings/wasm/identity_wasm/src/storage/wasm_storage.rs index 72d2657950..7e75091855 100644 --- a/bindings/wasm/src/storage/wasm_storage.rs +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage.rs @@ -1,7 +1,6 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 - -use std::rc::Rc; +use std::sync::Arc; use identity_iota::storage::storage::Storage; @@ -14,7 +13,7 @@ pub(crate) type WasmStorageInner = Storage; /// A type wrapping a `JwkStorage` and `KeyIdStorage` that should always be used together when /// working with storage backed DID documents. #[wasm_bindgen(js_name = Storage)] -pub struct WasmStorage(pub(crate) Rc); +pub struct WasmStorage(pub(crate) Arc); #[wasm_bindgen(js_class = Storage)] impl WasmStorage { @@ -22,7 +21,7 @@ impl WasmStorage { #[wasm_bindgen(constructor)] #[allow(non_snake_case)] pub fn new(jwkStorage: WasmJwkStorage, keyIdStorage: WasmKeyIdStorage) -> WasmStorage { - WasmStorage(Rc::new(Storage::new(jwkStorage, keyIdStorage))) + WasmStorage(Arc::new(Storage::new(jwkStorage, keyIdStorage))) } /// Obtain the wrapped `KeyIdStorage`. diff --git a/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs new file mode 100644 index 0000000000..8c8899aaf3 --- /dev/null +++ b/bindings/wasm/identity_wasm/src/storage/wasm_storage_signer.rs @@ -0,0 +1,62 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::borrow::Borrow; + +use identity_iota::iota_interaction::IotaKeySignature; +use identity_iota::storage::KeyId; +use identity_iota::storage::Storage; +use identity_iota::storage::StorageSigner; +use identity_iota::verification::jwk::Jwk; +use identity_iota::verification::jwk::JwkParams; +use identity_iota::verification::jwu; +use secret_storage::SignatureScheme; +use secret_storage::Signer; +use wasm_bindgen::prelude::*; + +use crate::error::wasm_error; +use crate::error::Result; +use crate::jose::WasmJwk; +use crate::storage::WasmJwkStorage; +use crate::storage::WasmKeyIdStorage; +use crate::storage::WasmStorage; + +#[wasm_bindgen(js_name = StorageSigner)] +pub struct WasmStorageSigner(pub(crate) StorageSigner); + +#[wasm_bindgen(js_class = StorageSigner)] +impl WasmStorageSigner { + #[wasm_bindgen(constructor)] + pub fn new(storage: &WasmStorage, key_id: String, public_key: WasmJwk) -> Self { + let signer = StorageSigner::new_with_shared_storage( + storage.0.clone(), + KeyId::new(&key_id), + public_key.0, + ); + Self(signer) + } + + #[wasm_bindgen(js_name = keyId)] + pub fn key_id(&self) -> String { + self.0.key_id().to_string() + } + + #[wasm_bindgen(js_name = sign)] + pub async fn sign( + &self, + data: &[u8], + ) -> Result<::Signature> { + self.0.sign(data).await.map_err(wasm_error) + } + + #[wasm_bindgen(js_name = publicKeyRaw)] + pub fn public_key_raw(&self) -> Result<::PublicKey> { + let jwk = self.0.public_key().clone(); + + match jwk.params() { + JwkParams::Okp(params) => jwu::decode_b64(¶ms.x) + .map_err(|e| JsValue::from_str(&format!("could not base64 decode key {}; {e}", self.key_id()))), + _ => todo!("add support for other key types"), + } + } +} diff --git a/bindings/wasm/src/verification/custom_verification.rs b/bindings/wasm/identity_wasm/src/verification/custom_verification.rs similarity index 100% rename from bindings/wasm/src/verification/custom_verification.rs rename to bindings/wasm/identity_wasm/src/verification/custom_verification.rs diff --git a/bindings/wasm/src/verification/jws_verifier.rs b/bindings/wasm/identity_wasm/src/verification/jws_verifier.rs similarity index 100% rename from bindings/wasm/src/verification/jws_verifier.rs rename to bindings/wasm/identity_wasm/src/verification/jws_verifier.rs diff --git a/bindings/wasm/src/verification/mod.rs b/bindings/wasm/identity_wasm/src/verification/mod.rs similarity index 100% rename from bindings/wasm/src/verification/mod.rs rename to bindings/wasm/identity_wasm/src/verification/mod.rs diff --git a/bindings/wasm/src/verification/wasm_method_data.rs b/bindings/wasm/identity_wasm/src/verification/wasm_method_data.rs similarity index 100% rename from bindings/wasm/src/verification/wasm_method_data.rs rename to bindings/wasm/identity_wasm/src/verification/wasm_method_data.rs diff --git a/bindings/wasm/src/verification/wasm_method_relationship.rs b/bindings/wasm/identity_wasm/src/verification/wasm_method_relationship.rs similarity index 100% rename from bindings/wasm/src/verification/wasm_method_relationship.rs rename to bindings/wasm/identity_wasm/src/verification/wasm_method_relationship.rs diff --git a/bindings/wasm/src/verification/wasm_method_scope.rs b/bindings/wasm/identity_wasm/src/verification/wasm_method_scope.rs similarity index 100% rename from bindings/wasm/src/verification/wasm_method_scope.rs rename to bindings/wasm/identity_wasm/src/verification/wasm_method_scope.rs diff --git a/bindings/wasm/src/verification/wasm_method_type.rs b/bindings/wasm/identity_wasm/src/verification/wasm_method_type.rs similarity index 100% rename from bindings/wasm/src/verification/wasm_method_type.rs rename to bindings/wasm/identity_wasm/src/verification/wasm_method_type.rs diff --git a/bindings/wasm/src/verification/wasm_verification_method.rs b/bindings/wasm/identity_wasm/src/verification/wasm_verification_method.rs similarity index 100% rename from bindings/wasm/src/verification/wasm_verification_method.rs rename to bindings/wasm/identity_wasm/src/verification/wasm_verification_method.rs diff --git a/bindings/wasm/tests/core.ts b/bindings/wasm/identity_wasm/tests/core.ts similarity index 100% rename from bindings/wasm/tests/core.ts rename to bindings/wasm/identity_wasm/tests/core.ts diff --git a/bindings/wasm/tests/credentials.ts b/bindings/wasm/identity_wasm/tests/credentials.ts similarity index 100% rename from bindings/wasm/tests/credentials.ts rename to bindings/wasm/identity_wasm/tests/credentials.ts diff --git a/bindings/wasm/tests/iota.ts b/bindings/wasm/identity_wasm/tests/iota.ts similarity index 100% rename from bindings/wasm/tests/iota.ts rename to bindings/wasm/identity_wasm/tests/iota.ts diff --git a/bindings/wasm/tests/jose.ts b/bindings/wasm/identity_wasm/tests/jose.ts similarity index 100% rename from bindings/wasm/tests/jose.ts rename to bindings/wasm/identity_wasm/tests/jose.ts diff --git a/bindings/wasm/tests/jwk_storage.ts b/bindings/wasm/identity_wasm/tests/jwk_storage.ts similarity index 100% rename from bindings/wasm/tests/jwk_storage.ts rename to bindings/wasm/identity_wasm/tests/jwk_storage.ts diff --git a/bindings/wasm/tests/key_id_storage.ts b/bindings/wasm/identity_wasm/tests/key_id_storage.ts similarity index 100% rename from bindings/wasm/tests/key_id_storage.ts rename to bindings/wasm/identity_wasm/tests/key_id_storage.ts diff --git a/bindings/wasm/tests/resolver.ts b/bindings/wasm/identity_wasm/tests/resolver.ts similarity index 100% rename from bindings/wasm/tests/resolver.ts rename to bindings/wasm/identity_wasm/tests/resolver.ts diff --git a/bindings/wasm/tests/sd_jwt.ts b/bindings/wasm/identity_wasm/tests/sd_jwt.ts similarity index 100% rename from bindings/wasm/tests/sd_jwt.ts rename to bindings/wasm/identity_wasm/tests/sd_jwt.ts diff --git a/bindings/wasm/tests/storage.ts b/bindings/wasm/identity_wasm/tests/storage.ts similarity index 100% rename from bindings/wasm/tests/storage.ts rename to bindings/wasm/identity_wasm/tests/storage.ts diff --git a/bindings/wasm/tests/txm_readme.js b/bindings/wasm/identity_wasm/tests/txm_readme.js similarity index 100% rename from bindings/wasm/tests/txm_readme.js rename to bindings/wasm/identity_wasm/tests/txm_readme.js diff --git a/bindings/wasm/tests/txm_readme_rust.js b/bindings/wasm/identity_wasm/tests/txm_readme_rust.js similarity index 90% rename from bindings/wasm/tests/txm_readme_rust.js rename to bindings/wasm/identity_wasm/tests/txm_readme_rust.js index d024653fe2..35a49748a2 100644 --- a/bindings/wasm/tests/txm_readme_rust.js +++ b/bindings/wasm/identity_wasm/tests/txm_readme_rust.js @@ -3,7 +3,7 @@ const spawn = require("child_process").spawn; describe("Test TXM", () => { before((done) => { - let process = spawn("txm", ["../../README.md"]); + let process = spawn("txm", ["../../../README.md"]); process.stdout.on("data", function(data) { console.log(data.toString()); }); diff --git a/bindings/wasm/identity_wasm/tsconfig.json b/bindings/wasm/identity_wasm/tsconfig.json new file mode 100644 index 0000000000..c499a54bc9 --- /dev/null +++ b/bindings/wasm/identity_wasm/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../tsconfig.json", + "entryPoints": [ + "./node/" + ], + "tsconfig": "./tsconfig.typedoc.json", + "out": "./docs/wasm", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@iota/identity-wasm/*": [ + "./*" + ], + "@iota/iota-interaction-ts/*": [ + "../iota_interaction_ts/*" + ] + } + } +} diff --git a/bindings/wasm/identity_wasm/tsconfig.node.json b/bindings/wasm/identity_wasm/tsconfig.node.json new file mode 100644 index 0000000000..7eaa618102 --- /dev/null +++ b/bindings/wasm/identity_wasm/tsconfig.node.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "ES2020", + "esModuleInterop": true, + "module": "commonjs" + } +} diff --git a/bindings/wasm/tsconfig.typedoc.json b/bindings/wasm/identity_wasm/tsconfig.typedoc.json similarity index 53% rename from bindings/wasm/tsconfig.typedoc.json rename to bindings/wasm/identity_wasm/tsconfig.typedoc.json index 02841974ec..bfc43be938 100644 --- a/bindings/wasm/tsconfig.typedoc.json +++ b/bindings/wasm/identity_wasm/tsconfig.typedoc.json @@ -1,4 +1,6 @@ { "extends": "./tsconfig.node.json", - "include": ["node/**/*"] + "include": [ + "node/**/*" + ] } diff --git a/bindings/wasm/identity_wasm/typedoc.json b/bindings/wasm/identity_wasm/typedoc.json new file mode 100644 index 0000000000..0eac1cd20a --- /dev/null +++ b/bindings/wasm/identity_wasm/typedoc.json @@ -0,0 +1,10 @@ +{ + "extends": [ + "../typedoc.json" + ], + "entryPoints": [ + "./node/" + ], + "tsconfig": "./tsconfig.typedoc.json", + "out": "./docs/wasm" +} \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml new file mode 100644 index 0000000000..3dce460b73 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "iota_interaction_ts" +version = "1.4.0" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +keywords = ["iota", "tangle", "identity", "wasm"] +license.workspace = true +publish = false +readme = "./README.md" +repository.workspace = true +rust-version.workspace = true +description = "identity_iota_interaction Adapters using Web Assembly bindings." + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +anyhow = "1.0.75" +async-trait = { version = "0.1", default-features = false } +bls12_381_plus = "0.8.17" +cfg-if = "1.0.0" +console_error_panic_hook = { version = "0.1" } +fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto" } +futures = { version = "0.3" } +js-sys = { version = "0.3.61" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.1.0" } +serde = { version = "1.0", features = ["derive"] } +serde-wasm-bindgen = "0.6.5" +serde_json = { version = "1.0", default-features = false } +thiserror.workspace = true +tsify = "0.4.5" +wasm-bindgen = { version = "=0.2.93", features = ["serde-serialize"] } +wasm-bindgen-futures = { version = "0.4", default-features = false } +zkryptium = "0.2.2" + +[dependencies.identity_iota_interaction] +path = "../../../identity_iota_interaction" +default-features = false + +[dependencies.identity_core] +path = "../../../identity_core" + +[dev-dependencies] +rand = "0.8.5" + +[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] +getrandom = { version = "0.2", default-features = false, features = ["js"] } +instant = { version = "0.1", default-features = false, features = ["wasm-bindgen"] } + +[lints.clippy] +# can be removed as soon as fix has been added to clippy +# see https://github.com/rust-lang/rust-clippy/issues/12377 +empty_docs = "allow" diff --git a/bindings/wasm/iota_interaction_ts/README.md b/bindings/wasm/iota_interaction_ts/README.md new file mode 100644 index 0000000000..6f7839c274 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/README.md @@ -0,0 +1,3 @@ +# Web Assembly adapters for identity_iota_interaction + +WASM bindings importing types from the IOTA Client typescript SDK to be used in the Identity library Rust code. \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts new file mode 100644 index 0000000000..3cfae10e95 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -0,0 +1,164 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { + CoinStruct, + ExecutionStatus, + IotaClient, + IotaTransactionBlockResponse, + OwnedObjectRef, +} from "@iota/iota.js/client"; +import { messageWithIntent, toSerializedSignature } from "@iota/iota.js/cryptography"; +import { Ed25519PublicKey } from "@iota/iota.js/keypairs/ed25519"; +import { TransactionDataBuilder } from "@iota/iota.js/transactions"; +import { blake2b } from "@noble/hashes/blake2b"; + +export type Signer = { sign(data: Uint8Array): Promise }; + +export class IotaTransactionBlockResponseAdapter { + response: IotaTransactionBlockResponse; + + constructor(response: IotaTransactionBlockResponse) { + this.response = response; + } + + effects_is_none(): boolean { + return this.response.effects == null; + } + + effects_is_some(): boolean { + return !(typeof this.response.effects == null); + } + + to_string(): string { + return JSON.stringify(this.response); + } + + effects_execution_status_inner(): null | ExecutionStatus { + return this.response.effects != null ? this.response.effects.status : null; + } + + effects_created_inner(): null | OwnedObjectRef[] { + return this.response.effects != null && this.response.effects.created != null + ? this.response.effects.created + : null; + } +} + +async function signTransactionData( + txBcs: Uint8Array, + senderPublicKey: Uint8Array, + signer: { sign(data: Uint8Array): Promise }, +): Promise { + const intent = "TransactionData"; + const intentMessage = messageWithIntent(intent, txBcs); + const digest = blake2b(intentMessage, { dkLen: 32 }); + const signerSignature = await signer.sign(digest); + const signature = toSerializedSignature({ + signature: await signerSignature, + signatureScheme: "ED25519", + publicKey: new Ed25519PublicKey(senderPublicKey), + }); + + return signature; +} + +async function getCoinForTransaction(iotaClient: IotaClient, senderAddress: string): Promise { + const coins = await iotaClient.getCoins({ owner: senderAddress }); + if (coins.data.length === 0) { + throw new Error("could not find coins for transaction"); + } + + return coins.data[1]; +} + +async function addGasDataToTransaction( + iotaClient: IotaClient, + senderAddress: string, + txBcs: Uint8Array, + gasBudget?: bigint, +): Promise { + const gasPrice = await iotaClient.getReferenceGasPrice(); + const gasCoin = await getCoinForTransaction(iotaClient, senderAddress); + const txData = TransactionDataBuilder.fromBytes(txBcs); + const gasData = { + budget: gasBudget ? gasBudget.toString() : "50000000000", // 50_000_000_000 + owner: senderAddress, + payment: [{ + objectId: gasCoin.coinObjectId, + version: gasCoin.version, + digest: gasCoin.digest, + }], + price: gasPrice.toString(), + }; + let builtTx = txData.build({ + overrides: { + gasData, + sender: senderAddress, + }, + }); + + if (!gasBudget) { + // no budget given, so we have to estimate gas usage + const dryRunGasResult = (await iotaClient + .dryRunTransactionBlock({ transactionBlock: builtTx })).effects; + if (dryRunGasResult.status.status === "failure") { + throw new Error("transaction returned an unexpected response; " + dryRunGasResult.status.error); + } + + const gasSummary = dryRunGasResult.gasUsed; + const overhead = gasPrice * BigInt(1000); + let netUsed = BigInt(gasSummary.computationCost) + + BigInt(gasSummary.storageCost) + - BigInt(gasSummary.storageRebate); + netUsed = netUsed >= 0 ? netUsed : BigInt(0); + const computation = BigInt(gasSummary.computationCost); + const maxCost = netUsed > computation ? netUsed : computation; + const budget = overhead + maxCost; + + gasData.budget = budget.toString(); + builtTx = txData.build({ + overrides: { + gasData, + sender: senderAddress, + }, + }); + } + + return builtTx; +} + +// estimate gas, get coin, execute tx here +export async function executeTransaction( + iotaClient: IotaClient, + senderAddress: string, + senderPublicKey: Uint8Array, + txBcs: Uint8Array, + signer: Signer, + gasBudget?: bigint, +): Promise { + const txWithGasData = await addGasDataToTransaction(iotaClient, senderAddress, txBcs, gasBudget); + const signature = await signTransactionData(txWithGasData, senderPublicKey, signer); + console.log(signature); + + const response = await iotaClient.executeTransactionBlock({ + transactionBlock: txWithGasData, + signature, + options: { // `IotaTransactionBlockResponseOptions::full_content()` + showEffects: true, + showInput: true, + showRawInput: true, + showEvents: true, + showObjectChanges: true, + showBalanceChanges: true, + showRawEffects: false, + }, + }); + console.dir(response); + + if (response?.effects?.status.status === "failure") { + throw new Error(`transaction returned an unexpected response; ${response?.effects?.status.error}`); + } + + return new IotaTransactionBlockResponseAdapter(response); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/tsconfig.json b/bindings/wasm/iota_interaction_ts/lib/tsconfig.json new file mode 100644 index 0000000000..08004d04ef --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../tsconfig.node.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "~identity_wasm": [ + "../node/identity_wasm", + "./identity_wasm.js" + ], + "~sdk-wasm": [ + "../node_modules/@iota/sdk-wasm/node", + "@iota/sdk-wasm/node" + ], + "../lib": [ + "." + ] + }, + "outDir": "../node", + "declarationDir": "../node" + } +} diff --git a/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json b/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json new file mode 100644 index 0000000000..0d87cb28af --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json @@ -0,0 +1,22 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "~identity_wasm": [ + "../web/identity_wasm", + "./identity_wasm.js" + ], + "~sdk-wasm": [ + "../node_modules/@iota/sdk-wasm/web", + "@iota/sdk-wasm/web" + ], + "../lib": [ + "." + ] + }, + "outDir": "../web", + "declarationDir": "../web", + "module": "ES2020" + } +} diff --git a/bindings/wasm/iota_interaction_ts/package-lock.json b/bindings/wasm/iota_interaction_ts/package-lock.json new file mode 100644 index 0000000000..2a9def84c7 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/package-lock.json @@ -0,0 +1,7353 @@ +{ + "name": "@iota/iota-interaction-ts", + "version": "1.4.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@iota/iota-interaction-ts", + "version": "1.4.0", + "license": "Apache-2.0", + "dependencies": { + "@iota/iota.js": "file:../../../../iota/sdk/typescript", + "@noble/ed25519": "^1.7.3", + "@noble/hashes": "^1.4.0", + "@types/node-fetch": "^2.6.2", + "base64-arraybuffer": "^1.0.2", + "node-fetch": "^2.6.7" + }, + "devDependencies": { + "@transmute/did-key-ed25519": "0.3.0-unstable.9", + "@types/mocha": "^9.1.0", + "@types/node": "^22.0.0", + "big-integer": "^1.6.51", + "copy-webpack-plugin": "^7.0.0", + "cypress": "^13.12.0", + "cypress-parallel": "^0.14.0", + "dprint": "^0.33.0", + "fs-extra": "^10.1.0", + "jsdoc-to-markdown": "^7.1.1", + "mocha": "^9.2.0", + "ts-mocha": "^9.0.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.1.0", + "txm": "^8.1.0", + "typedoc": "^0.24.6", + "typedoc-plugin-markdown": "^3.14.0", + "typescript": "=5.1", + "wasm-opt": "^1.3.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@iota/sdk-wasm": "^1.0.4" + } + }, + "../../../../iota/sdk/typescript": { + "name": "@iota/iota-sdk", + "version": "0.3.0", + "license": "Apache-2.0", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0", + "@iota/bcs": "workspace:*", + "@noble/curves": "^1.4.2", + "@noble/hashes": "^1.4.0", + "@scure/bip32": "^1.4.0", + "@scure/bip39": "^1.3.0", + "@suchipi/femver": "^1.0.0", + "bech32": "^2.0.0", + "gql.tada": "^1.8.2", + "graphql": "^16.9.0", + "tweetnacl": "^1.0.3", + "valibot": "^0.36.0" + }, + "devDependencies": { + "@0no-co/graphqlsp": "^1.12.11", + "@graphql-codegen/add": "^5.0.3", + "@graphql-codegen/cli": "^5.0.2", + "@graphql-codegen/typed-document-node": "^5.0.9", + "@graphql-codegen/typescript": "4.0.9", + "@graphql-codegen/typescript-operations": "^4.2.3", + "@iarna/toml": "^2.2.5", + "@iota/build-scripts": "workspace:^", + "@types/node": "^20.14.10", + "@types/tmp": "^0.2.6", + "@types/ws": "^8.5.10", + "cross-env": "^7.0.3", + "dotenv": "^16.4.5", + "graphql-config": "^5.0.3", + "msw": "^2.3.1", + "tmp": "^0.2.3", + "ts-retry-promise": "^0.8.1", + "typescript": "^5.5.3", + "vite": "^5.3.3", + "vitest": "^2.0.1", + "wait-on": "^7.2.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20" + } + }, + "../../../../iotaledger/iota/sdk/typescript": { + "extraneous": true + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cypress/request": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.6.tgz", + "integrity": "sha512-fi0eVdCOtKu5Ed6+E8mYxUF6ZTFJDZvHogCBelM0xVXmrDEkyM22gRArQzq1YcHPm1V47Vf/iAD+WgVdUlJCGg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "6.13.0", + "safe-buffer": "^5.1.2", + "tough-cookie": "^5.0.0", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/request/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + } + }, + "node_modules/@cypress/xvfb/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@did-core/data-model": { + "version": "0.1.1-unstable.15", + "resolved": "https://registry.npmjs.org/@did-core/data-model/-/data-model-0.1.1-unstable.15.tgz", + "integrity": "sha512-l7gxLxegcXW7389G+j6o+S24lS8uasmJx5txWpW3QadNvOawKwvWn8bV59SdHSK806xNzIZaCLKmXKxebs8yAQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "factory.ts": "^0.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@did-core/did-ld-json": { + "version": "0.1.1-unstable.15", + "resolved": "https://registry.npmjs.org/@did-core/did-ld-json/-/did-ld-json-0.1.1-unstable.15.tgz", + "integrity": "sha512-p2jKRxSU+eJJqd+ewCklYp/XZ6ysISk8VU2/kANCoB/WwUy/kVgw2rUNScRDXw2utr9Qj36P8EZTYi4aj7vRCQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@transmute/did-context": "^0.6.1-unstable.25", + "jsonld-checker": "^0.1.6" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@digitalbazaar/http-client": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-1.2.0.tgz", + "integrity": "sha512-W9KQQ5pUJcaR0I4c2HPJC0a7kRbZApIorZgPnEDwMBgj16iQzutGLrCXYaZOmxqVLVNqqlQ4aUJh+HBQZy4W6Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "esm": "^3.2.22", + "ky": "^0.25.1", + "ky-universal": "^0.8.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@iota/iota.js": { + "resolved": "../../../../iota/sdk/typescript", + "link": true + }, + "node_modules/@iota/sdk-wasm": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@iota/sdk-wasm/-/sdk-wasm-1.1.3.tgz", + "integrity": "sha512-piyl0B6gcoo7mbmX3QUCyEYtqk6UoCS2cqBYiV7FFz3fmT2DPcQJmcaDvW0nmNh5BbRR9MhPkp3MEerPm6mezA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "class-transformer": "^0.5.1", + "node-fetch": "^2.6.7", + "qs": "^6.9.7", + "reflect-metadata": "^0.1.13", + "semver": "^7.5.2", + "text-encoding": "^0.7.0" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.8.tgz", + "integrity": "sha512-5e+SFVavj1ORKlKaKr2BmTOekmXbelU7dC0cDkQLqag7xfuTPuGMUFx7KWJuv4bYZrTsoL2Z18VVCOKYxzoHcg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@noble/ed25519": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", + "integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@noble/hashes": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", + "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/int": "^1.0.1" + } + }, + "node_modules/@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/bytes": "^1.0.1" + } + }, + "node_modules/@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@transmute/did-context": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/did-context/-/did-context-0.6.1-unstable.37.tgz", + "integrity": "sha512-p/QnG3QKS4218hjIDgdvJOFATCXsAnZKgy4egqRrJLlo3Y6OaDBg7cA73dixOwUPoEKob0K6rLIGcsCI/L1acw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@transmute/did-key-common": { + "version": "0.3.0-unstable.10", + "resolved": "https://registry.npmjs.org/@transmute/did-key-common/-/did-key-common-0.3.0-unstable.10.tgz", + "integrity": "sha512-Iryh/HcGIvmTtWFTRaG/JEgbUsqI5OqKqkR2676yQWK4ajLMsyNattz5n0ZfFQk/4U7Ee6pJvvKRduFDAqqV0Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@did-core/data-model": "^0.1.1-unstable.13", + "@did-core/did-ld-json": "^0.1.1-unstable.13", + "@transmute/did-context": "^0.6.1-unstable.36", + "@transmute/ld-key-pair": "^0.6.1-unstable.36", + "@transmute/security-context": "^0.6.1-unstable.36" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@transmute/did-key-ed25519": { + "version": "0.3.0-unstable.9", + "resolved": "https://registry.npmjs.org/@transmute/did-key-ed25519/-/did-key-ed25519-0.3.0-unstable.9.tgz", + "integrity": "sha512-mFTTL1IHp26JweHN/SCj2Re5iBr5sWbyctd5LRoHRU1DQB0XmBBFX5ZzCCtnEiBGvDF55Eyx1vpkwUHIcD3QGg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@transmute/did-key-common": "^0.3.0-unstable.9", + "@transmute/ed25519-key-pair": "^0.6.1-unstable.37" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@transmute/ed25519-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/ed25519-key-pair/-/ed25519-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-l34yzE/QnQwmdk5xY9g2kD55e4XPp/jTZQzPu7I6J4Ar+bMaL/0RLL/pgvwyI7qUpsddxRf4WPZCCcZveqPcdA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stablelib/ed25519": "^1.0.1", + "@transmute/ld-key-pair": "^0.6.1-unstable.37", + "@transmute/x25519-key-pair": "^0.6.1-unstable.37" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@transmute/ld-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/ld-key-pair/-/ld-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-DcTpEruAQBfOd2laZkg3uCQ+67Y7dw2hsvo42NAQ5tItCIx5AClP7zccri7T2JUcfDUFaE32z/BLTMEKYt3XZQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@transmute/security-context": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/security-context/-/security-context-0.6.1-unstable.37.tgz", + "integrity": "sha512-GtLmG65qlORrz/2S4I74DT+vA4+qXsFxrMr0cNOXjUqZBd/AW1PTrFnryLF9907BfoiD58HC9qb1WVGWjSlBYw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@transmute/x25519-key-pair": { + "version": "0.6.1-unstable.37", + "resolved": "https://registry.npmjs.org/@transmute/x25519-key-pair/-/x25519-key-pair-0.6.1-unstable.37.tgz", + "integrity": "sha512-j6zR9IoJmgVhUCVH8YVGpsgQf99SxPKZ00LGnUheBAQzgj2lULGBQ44G+GqBCdzfT0qweptTfp1RjqqHEpizeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stablelib/x25519": "^1.0.0", + "@transmute/ld-key-pair": "^0.6.1-unstable.37" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sizzle": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz", + "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escape-sequences": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", + "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ansi-escape-sequences/node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cache-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", + "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^4.0.1", + "fs-then-native": "^2.0.0", + "mkdirp2": "^1.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cache-point/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cachedir": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001686", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001686.tgz", + "integrity": "sha512-Y7deg0Aergpa24M3qLC5xjNklnKnhsmSyR/V89dLZ1n0ucJIFNs7PgR2Yfa/Zf6W79SbBicgtGxZr2juHkEUIA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0", + "peer": true + }, + "node_modules/canonicalize": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", + "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT", + "peer": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/collect-all": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.4.tgz", + "integrity": "sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "stream-connect": "^1.0.2", + "stream-via": "^1.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-args/node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/command-line-args/node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-tool": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", + "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escape-sequences": "^4.0.0", + "array-back": "^2.0.0", + "command-line-args": "^5.0.0", + "command-line-usage": "^4.1.0", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-tool/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", + "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escape-sequences": "^4.0.0", + "array-back": "^2.0.0", + "table-layout": "^0.4.2", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-sequence": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", + "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-master": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", + "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-back": "^2.0.1" + } + }, + "node_modules/config-master/node_modules/walk-back": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", + "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-7.0.0.tgz", + "integrity": "sha512-SLjQNa5iE3BoCP76ESU9qYo9ZkEWtXoZxDurHoqPchAFRblJ9g96xTeC560UXBMre1Nx6ixIIUfiY3VcjpJw3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.4", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cypress": { + "version": "13.16.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.16.1.tgz", + "integrity": "sha512-17FtCaz0cx7ssWYKXzGB0Vub8xHwpVPr+iPt2fHhLMDhVAPVrplD+rTQsZUsfb19LVBn5iwkEUFjQ1yVVJXsLA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@cypress/request": "^3.0.6", + "@cypress/xvfb": "^1.2.4", + "@types/sinonjs__fake-timers": "8.1.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "buffer": "^5.7.1", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "ci-info": "^4.0.0", + "cli-cursor": "^3.1.0", + "cli-table3": "~0.6.1", + "commander": "^6.2.1", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.4", + "enquirer": "^2.3.6", + "eventemitter2": "6.4.7", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.8", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "process": "^0.11.10", + "proxy-from-env": "1.0.0", + "request-progress": "^3.0.0", + "semver": "^7.5.3", + "supports-color": "^8.1.1", + "tmp": "~0.2.3", + "tree-kill": "1.2.2", + "untildify": "^4.0.0", + "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" + }, + "engines": { + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" + } + }, + "node_modules/cypress-multi-reporters": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.4.tgz", + "integrity": "sha512-3xU2t6pZjZy/ORHaCvci5OT1DAboS4UuMMM8NBAizeb2C9qmHt+cgAjXgurazkwkPRdO7ccK39M5ZaPCju0r6A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.3.4", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "mocha": ">=3.1.2" + } + }, + "node_modules/cypress-parallel": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.14.0.tgz", + "integrity": "sha512-Lsh28G70vxjL0cjR820BdaVQHnGc17Vvb+tYmjbRPmfC+XEzwvUzhcaD0E1zCztBSYhw+b1/1JLmW4Y0qE/EDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.5.0", + "cli-table3": "^0.6.0", + "cross-spawn": "^7.0.3", + "fs-extra": "^10.0.0", + "glob-escape": "^0.0.2", + "is-npm": "^5.0.0", + "lodash.camelcase": "^4.3.0", + "mocha": "~9.2.0", + "yargs": "15.3.1" + }, + "bin": { + "cypress-parallel": "cli.js" + }, + "peerDependencies": { + "cypress-multi-reporters": "^1.5.0" + } + }, + "node_modules/cypress/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dmd": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.2.3.tgz", + "integrity": "sha512-SIEkjrG7cZ9GWZQYk/mH+mWtcRPly/3ibVuXO/tP/MFoWz6KiRK77tSMq6YQBPl7RljPtXPQ/JhxbNuCdi1bNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "cache-point": "^2.0.0", + "common-sequence": "^2.0.2", + "file-set": "^4.0.2", + "handlebars": "^4.7.8", + "marked": "^4.3.0", + "object-get": "^2.1.1", + "reduce-flatten": "^3.0.1", + "reduce-unique": "^2.0.1", + "reduce-without": "^1.0.1", + "test-value": "^3.0.0", + "walk-back": "^5.1.0" + }, + "engines": { + "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, + "license": "MIT", + "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", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.70", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.70.tgz", + "integrity": "sha512-P6FPqAWIZrC3sHDAwBitJBs7N7IF58m39XVny7DFseQXK2eiMn7nNQizFf63mWDDUnFvaqsM8FI0+ZZfLkdUGA==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter2": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/factory.ts": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/factory.ts/-/factory.ts-0.5.2.tgz", + "integrity": "sha512-I4YDKuyMW+s2PocnWh/Ekv9wSStt/MNN1ZRb1qhy0Kv056ndlzbLHDsW9KEmTAqMpLI3BtjSqEdZ7ZfdnaXn9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "source-map-support": "^0.5.19" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-blob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz", + "integrity": "sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10.17.0 || >=12.3.0" + }, + "peerDependenciesMeta": { + "domexception": { + "optional": true + } + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-set": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.2.tgz", + "integrity": "sha512-fuxEgzk4L8waGXaAkd8cMr73Pm0FxOVkn8hztzUW7BAHhOGH90viQNXbiOsnecCWmfInqU6YmAMwxRMdKETceQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^5.0.0", + "glob": "^7.1.6" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/file-set/node_modules/array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-replace/node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-then-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", + "integrity": "sha512-X712jAOaWXkemQCAmWeg5rOT2i+KOpWz1Z/txk/cW0qlOu2oQ9H61vc5w3X/iyuUEfq/OyaFJ78/cZAQD1/bgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-escape": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz", + "integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.x" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.1.0.tgz", + "integrity": "sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdoc": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc-api": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-7.2.0.tgz", + "integrity": "sha512-93YDnlm/OYTlLOFeNs4qAv0RBCJ0kGj67xQaWy8wrbk97Rw1EySitoOTHsTHXPEs3uyx2IStPKGrbE7LTnZXbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "cache-point": "^2.0.0", + "collect-all": "^1.0.4", + "file-set": "^4.0.2", + "fs-then-native": "^2.0.0", + "jsdoc": "^4.0.0", + "object-to-spawn-args": "^2.0.1", + "temp-path": "^1.0.0", + "walk-back": "^5.1.0" + }, + "engines": { + "node": ">=12.17" + } + }, + "node_modules/jsdoc-parse": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.4.tgz", + "integrity": "sha512-MQA+lCe3ioZd0uGbyB3nDCDZcKgKC7m/Ivt0LgKZdUoOlMJxUWJQ3WI6GeyHp9ouznKaCjlp7CU9sw5k46yZTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "find-replace": "^5.0.1", + "lodash.omit": "^4.5.0", + "sort-array": "^5.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdoc-parse/node_modules/find-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/jsdoc-to-markdown": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.1.1.tgz", + "integrity": "sha512-CI86d63xAVNO+ENumWwmJ034lYe5iGU5GwjtTA11EuphP9tpnoi4hrKgR/J8uME0D+o4KUpVfwX1fjZhc8dEtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "command-line-tool": "^0.8.0", + "config-master": "^3.1.0", + "dmd": "^6.1.0", + "jsdoc-api": "^7.1.1", + "jsdoc-parse": "^6.1.0", + "walk-back": "^5.1.0" + }, + "bin": { + "jsdoc2md": "bin/cli.js" + }, + "engines": { + "node": ">=12.17" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonld": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-5.2.0.tgz", + "integrity": "sha512-JymgT6Xzk5CHEmHuEyvoTNviEPxv6ihLWSPu1gFdtjSAyM6cFqNrv02yS/SIur3BBIkCf0HjizRc24d8/FfQKw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@digitalbazaar/http-client": "^1.1.0", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsonld-checker": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/jsonld-checker/-/jsonld-checker-0.1.8.tgz", + "integrity": "sha512-jclmnPRrm5SEpaIV6IiSTJxplRAqIWHduQLsUfrYpZM41Ng48m1RN2/aUyHze/ynfO0D2UhlJBt8SdObsH5GBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonld": "^5.2.0", + "node-fetch": "^2.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ky": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz", + "integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, + "node_modules/ky-universal": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz", + "integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "node-fetch": "3.0.0-beta.9" + }, + "engines": { + "node": ">=10.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky-universal?sponsor=1" + }, + "peerDependencies": { + "ky": ">=0.17.0", + "web-streams-polyfill": ">=2.0.0" + }, + "peerDependenciesMeta": { + "web-streams-polyfill": { + "optional": true + } + } + }, + "node_modules/ky-universal/node_modules/node-fetch": { + "version": "3.0.0-beta.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz", + "integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^3.0.1", + "fetch-blob": "^2.1.1" + }, + "engines": { + "node": "^10.17 || >=12.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "> 0.8" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/listr2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.1", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp2": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.5.tgz", + "integrity": "sha512-xOE9xbICroUDmG1ye2h4bZ8WBie9EGmACaco8K8cx6RlkJJrxGIqjGqztAI+NMhexXBcdGbSEzI6N3EJPevxZw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true, + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-get": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", + "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-to-spawn-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", + "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", + "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rdf-canonize": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-3.4.0.tgz", + "integrity": "sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "setimmediate": "^1.0.5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reduce-flatten": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.1.tgz", + "integrity": "sha512-bYo+97BmUUOzg09XwfkwALt4PQH1M5L0wzKerBt6WLm3Fhdd43mMS89HiT1B9pJIqko/6lWx3OnV4J9f2Kqp5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reduce-unique": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", + "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/reduce-without": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", + "integrity": "sha512-zQv5y/cf85sxvdrKPlfcRzlDn/OqKFThNimYmsS3flmkioKvkUGn2Qg9cJVoQiEvdxFGLE0MQER/9fZ9sUqdxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "test-value": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-without/node_modules/array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "typical": "^2.6.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/reduce-without/node_modules/test-value": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", + "integrity": "sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^1.0.3", + "typical": "^2.6.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "throttleit": "^1.0.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sort-array": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-5.0.0.tgz", + "integrity": "sha512-Sg9MzajSGprcSrMIxsXyNT0e0JB47RJRfJspC+7co4Z5BdNsNl8FmWI+lXEpyKq+vkMG6pHgAhqyCO+bkDTfFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "^0.1.1" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/sort-array/node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", + "integrity": "sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-connect/node_modules/array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha512-1WxbZvrmyhkNoeYcizokbmh5oiOCIfyvGtcqbK3Ls1v1fKcquzxnQSceOx6tzq7jmai2kFLWIpGND2cLhH6TPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "typical": "^2.6.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/stream-via": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", + "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "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, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/table-layout": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", + "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^2.0.0", + "deep-extend": "~0.6.0", + "lodash.padend": "^4.6.1", + "typical": "^2.6.1", + "wordwrapjs": "^3.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/temp-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", + "integrity": "sha512-TvmyH7kC6ZVTYkqCODjJIbgvu0FKiwQpZ4D1aknE7xpcDf/qEOB8KZEK5ef2pfbVoiBhNWs3yx4y+ESMtNYmlg==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/test-value": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", + "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^2.0.0", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/test-value/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-encoding": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "deprecated": "no longer maintained", + "license": "(Unlicense OR Apache-2.0)", + "peer": true + }, + "node_modules/throttleit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", + "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tldts": { + "version": "6.1.65", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.65.tgz", + "integrity": "sha512-xU9gLTfAGsADQ2PcWee6Hg8RFAv0DnjMGVJmDnUmI8a9+nYmapMQix4afwrdaCtT+AqP4MaxEzu7cCrYmBPbzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.65" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.65", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.65.tgz", + "integrity": "sha512-Uq5t0N0Oj4nQSbU8wFN1YYENvMthvwU13MQrMJRspYCGLSAZjAfoBOJki5IQpnBM/WFskxxC/gIOTwaedmHaSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-mocha": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-9.0.2.tgz", + "integrity": "sha512-WyQjvnzwrrubl0JT7EC1yWmNpcsU3fOuBFfdps30zbmFBgKniSaSOyZMZx+Wq7kytUs5CY+pEbSYEbGfIKnXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ts-node": "7.0.1" + }, + "bin": { + "ts-mocha": "bin/ts-mocha" + }, + "engines": { + "node": ">= 6.X.X" + }, + "optionalDependencies": { + "tsconfig-paths": "^3.5.0" + }, + "peerDependencies": { + "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X" + } + }, + "node_modules/ts-mocha/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ts-mocha/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "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", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ts-mocha/node_modules/ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ts-mocha/node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "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", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/txm": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/txm/-/txm-8.2.0.tgz", + "integrity": "sha512-ALNu1KIbUMXlsl/UacjfhbtG4CWK07JYtf0KNf8aXhoixmtgMj2MdOit5er6V8qh5eTIvtB6+AexIQMLZhSz6Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "async": "^3.2.1", + "diff-match-patch": "^1.0.5", + "kleur": "^4.1.4", + "remark-parse": "^10.0.1", + "supports-color": "^9.1.0", + "unified": "^10.1.1" + }, + "bin": { + "txm": "src/cli.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/txm/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedoc": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", + "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.0", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", + "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.24.0" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/walk-back": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.1.tgz", + "integrity": "sha512-e/FRLDVdZQWFrAzU6Hdvpm7D7m2ina833gIKLptQykRK49mmCYHLHq7UqjPDbxbKLZkTkW1rFqbengdE3sLfdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/wasm-opt": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/wasm-opt/-/wasm-opt-1.4.0.tgz", + "integrity": "sha512-wIsxxp0/FOSphokH4VOONy1zPkVREQfALN+/JTvJPK8gFSKbsmrcfECu2hT7OowqPfb4WEMSMceHgNL0ipFRyw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.9", + "tar": "^6.1.13" + }, + "bin": { + "wasm-opt": "bin/wasm-opt.js" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.97.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.0.tgz", + "integrity": "sha512-CWT8v7ShSfj7tGs4TLRtaOLmOCPWhoKEvp+eA7FVx8Xrjb3XfT0aXdxDItnRZmE8sHcH+a8ayDrJCOjXKxVFfQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wordwrapjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "reduce-flatten": "^1.0.1", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/reduce-flatten": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json new file mode 100644 index 0000000000..7e788f6757 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -0,0 +1,89 @@ +{ + "name": "@iota/iota-interaction-ts", + "version": "1.4.0", + "description": "WASM bindings importing types from the IOTA Client typescript SDK to be used in Rust", + "repository": { + "type": "git", + "url": "git+https://github.com/iotaledger/identity.rs.git" + }, + "directories": { + "example": "examples" + }, + "scripts": { + "build:src": "cargo build --lib --release --target wasm32-unknown-unknown", + "bundle:nodejs": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node iota_interaction_ts && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node iota_interaction_ts", + "bundle:web": "wasm-bindgen ../../../target/wasm32-unknown-unknown/release/iota_interaction_ts.wasm --typescript --target web --out-dir web && node ../build/web iota_interaction_ts && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web iota_interaction_ts", + "build:nodejs": "npm run build:src && npm run bundle:nodejs && wasm-opt -O node/iota_interaction_ts_bg.wasm -o node/iota_interaction_ts_bg.wasm", + "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/iota_interaction_ts_bg.wasm -o web/iota_interaction_ts_bg.wasm", + "build:docs": "typedoc && npm run fix_docs", + "build:examples:web": "tsc --project ./examples/tsconfig.web.json && node ../build/replace_paths ./examples/tsconfig.web.json ./examples/dist iota_interaction_ts resolve", + "build": "npm run build:web && npm run build:nodejs && npm run build:docs", + "example:node": "ts-node --project tsconfig.node.json -r tsconfig-paths/register ./examples/src/main.ts", + "test": "npm run test:unit:node && npm run test:readme && npm run test:node && test:browser:parallel", + "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:parallel:firefox": "cypress-parallel -s test:browser:firefox -t 4 -d cypress/e2e -a '\"--quiet\"'", + "test:browser:parallel:chrome": "cypress-parallel -s test:browser:chrome -t 4 -d cypress/e2e -a '\"--quiet\"'", + "test:browser": "cypress run --headless", + "test:browser:firefox": "cypress run --headless --browser firefox", + "test:browser:chrome": "cypress run --headless --browser chrome", + "test:readme": "mocha ./tests/txm_readme.js --retries 3 --timeout 180000 --exit", + "test:readme:rust": "mocha ./tests/txm_readme_rust.js --retries 3 --timeout 360000 --exit", + "test:unit:node": "ts-mocha -p tsconfig.node.json ./tests/*.ts --parallel --exit", + "cypress": "cypress open", + "fmt": "dprint fmt", + "fix_docs": "sed -Ei 's/(\\.md?#([^#]*)?)#/\\1/' ./docs/wasm/**/*.md" + }, + "config": { + "CYPRESS_VERIFY_TIMEOUT": 100000 + }, + "contributors": [ + ], + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/iotaledger/identity.rs/issues" + }, + "homepage": "https://www.iota.org", + "publishConfig": { + "access": "public" + }, + "files": [ + "web/*", + "node/*" + ], + "devDependencies": { + "@transmute/did-key-ed25519": "0.3.0-unstable.9", + "@types/node": "^22.0.0", + "@types/mocha": "^9.1.0", + "big-integer": "^1.6.51", + "copy-webpack-plugin": "^7.0.0", + "cypress": "^13.12.0", + "cypress-parallel": "^0.14.0", + "dprint": "^0.33.0", + "fs-extra": "^10.1.0", + "jsdoc-to-markdown": "^7.1.1", + "mocha": "^9.2.0", + "ts-mocha": "^9.0.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.1.0", + "txm": "^8.1.0", + "typedoc": "^0.24.6", + "typedoc-plugin-markdown": "^3.14.0", + "typescript": "=5.1", + "wasm-opt": "^1.3.0" + }, + "dependencies": { + "@iota/iota.js": "file:../../../../iota/sdk/typescript", + "@noble/ed25519": "^1.7.3", + "@noble/hashes": "^1.4.0", + "@types/node-fetch": "^2.6.2", + "base64-arraybuffer": "^1.0.2", + "node-fetch": "^2.6.7" + }, + "peerDependencies": { + "@iota/sdk-wasm": "^1.0.4" + }, + "engines": { + "node": ">=16" + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs new file mode 100644 index 0000000000..f09e1c5305 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs @@ -0,0 +1,81 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; + +use crate::error::TsSdkError; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::base_types::SequenceNumber; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::AssetMoveCalls; +use identity_iota_interaction::MoveType; +use identity_iota_interaction::ProgrammableTransactionBcs; + +pub struct AssetMoveCallsTsSdk {} + +impl AssetMoveCalls for AssetMoveCallsTsSdk { + type Error = TsSdkError; + + fn new_asset( + inner: T, + mutable: bool, + transferable: bool, + deletable: bool, + package: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn delete(asset: ObjectRef, package: ObjectID) -> Result { + unimplemented!(); + } + + fn transfer( + asset: ObjectRef, + recipient: IotaAddress, + package: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn make_tx( + proposal: (ObjectID, SequenceNumber), + cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + function_name: &'static str, + ) -> Result { + unimplemented!(); + } + + fn accept_proposal( + proposal: (ObjectID, SequenceNumber), + recipient_cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn conclude_or_cancel( + proposal: (ObjectID, SequenceNumber), + sender_cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn update( + asset: ObjectRef, + new_content: T, + package: ObjectID, + ) -> Result { + unimplemented!(); + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs new file mode 100644 index 0000000000..c04f573559 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs @@ -0,0 +1,10 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod types; +mod wasm_iota_client; +mod wasm_types; + +pub use types::*; +pub use wasm_iota_client::*; +pub use wasm_types::*; diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/types.rs new file mode 100644 index 0000000000..e751016f5b --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/types.rs @@ -0,0 +1,15 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use wasm_bindgen::prelude::*; + +pub use super::wasm_types::*; + +pub(crate) type WasmIotaAddress = String; +pub(crate) type WasmObjectID = String; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseBalance; +} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs new file mode 100644 index 0000000000..493d054d14 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs @@ -0,0 +1,375 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota_interaction::error::Error as IotaRpcError; +use identity_iota_interaction::error::IotaRpcResult; +use identity_iota_interaction::generated_types::ExecuteTransactionBlockParams; +use identity_iota_interaction::generated_types::GetCoinsParams; +use identity_iota_interaction::generated_types::GetDynamicFieldObjectParams; +use identity_iota_interaction::generated_types::GetObjectParams; +use identity_iota_interaction::generated_types::GetOwnedObjectsParams; +use identity_iota_interaction::generated_types::GetTransactionBlockParams; +use identity_iota_interaction::generated_types::QueryEventsParams; +use identity_iota_interaction::generated_types::SortOrder; +use identity_iota_interaction::generated_types::TryGetPastObjectParams; +use identity_iota_interaction::rpc_types::CoinPage; +use identity_iota_interaction::rpc_types::EventFilter; +use identity_iota_interaction::rpc_types::EventPage; +use identity_iota_interaction::rpc_types::IotaObjectDataOptions; +use identity_iota_interaction::rpc_types::IotaObjectResponse; +use identity_iota_interaction::rpc_types::IotaObjectResponseQuery; +use identity_iota_interaction::rpc_types::IotaPastObjectResponse; +use identity_iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; +use identity_iota_interaction::rpc_types::ObjectsPage; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::SequenceNumber; +use identity_iota_interaction::types::digests::TransactionDigest; +use identity_iota_interaction::types::dynamic_field::DynamicFieldName; +use identity_iota_interaction::types::event::EventID; +use identity_iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; +use identity_iota_interaction::SignatureBcs; +use identity_iota_interaction::TransactionDataBcs; +use js_sys::Promise; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; + +use super::wasm_types::IotaTransactionBlockResponseAdapter; +use super::wasm_types::PromiseIotaTransactionBlockResponse; +use super::wasm_types::WasmExecuteTransactionBlockParams; + +use crate::bindings::PromiseIotaObjectResponse; +use crate::bindings::PromiseObjectRead; +use crate::bindings::PromisePaginatedCoins; +use crate::bindings::PromisePaginatedEvents; +use crate::bindings::PromisePaginatedObjectsResponse; +use crate::bindings::WasmGetCoinsParams; +use crate::bindings::WasmGetDynamicFieldObjectParams; +use crate::bindings::WasmGetObjectParams; +use crate::bindings::WasmGetOwnedObjectsParams; +use crate::bindings::WasmGetTransactionBlockParams; +use crate::bindings::WasmQueryEventsParams; +use crate::bindings::WasmTryGetPastObjectParams; +use crate::common::types::PromiseString; +use crate::common::PromiseBigint; +use crate::console_log; +use crate::error::into_ts_sdk_result; +use crate::error::TsSdkError; + +// This file contains the wasm-bindgen 'glue code' providing +// the interface of the TS Iota client to rust code. + +// The typescript declarations imported in the following typescript_custom_section +// can be use as arguments for rust functions via the typescript_type annotation. +// In other words: The typescript_type "IotaClient" is imported here to be binded +// to the WasmIotaClient functions below. +// TODO: check why this isn't done by `module` macro attribute for `WasmKinesisClient` +#[wasm_bindgen(typescript_custom_section)] +const IOTA_CLIENT_TYPE: &'static str = r#" + import { IotaClient } from "@iota/iota.js/client"; +"#; + +#[wasm_bindgen(module = "@iota/iota.js/client")] +extern "C" { + #[wasm_bindgen(typescript_type = "IotaClient")] + #[derive(Clone)] + pub type WasmIotaClient; + + #[wasm_bindgen(method, js_name = getChainIdentifier)] + pub fn get_chain_identifier(this: &WasmIotaClient) -> PromiseString; + + #[wasm_bindgen(method, js_name = executeTransactionBlock)] + pub fn execute_transaction_block( + this: &WasmIotaClient, + params: &WasmExecuteTransactionBlockParams, + ) -> PromiseIotaTransactionBlockResponse; + + #[wasm_bindgen(method, js_name = getDynamicFieldObject)] + pub fn get_dynamic_field_object( + this: &WasmIotaClient, + input: &WasmGetDynamicFieldObjectParams, + ) -> PromiseIotaObjectResponse; + + #[wasm_bindgen(method, js_name = getObject)] + pub fn get_object(this: &WasmIotaClient, input: &WasmGetObjectParams) -> PromiseIotaObjectResponse; + + #[wasm_bindgen(method, js_name = getOwnedObjects)] + pub fn get_owned_objects(this: &WasmIotaClient, input: &WasmGetOwnedObjectsParams) + -> PromisePaginatedObjectsResponse; + + #[wasm_bindgen(method, js_name = getTransactionBlock)] + pub fn get_transaction_block( + this: &WasmIotaClient, + input: &WasmGetTransactionBlockParams, + ) -> PromiseIotaTransactionBlockResponse; + + #[wasm_bindgen(method, js_name = getReferenceGasPrice)] + pub fn get_reference_gas_price(this: &WasmIotaClient) -> PromiseBigint; + + #[wasm_bindgen(method, js_name = tryGetPastObject)] + pub fn try_get_past_object(this: &WasmIotaClient, input: &WasmTryGetPastObjectParams) -> PromiseObjectRead; + + #[wasm_bindgen(method, js_name = queryEvents)] + pub fn query_events(this: &WasmIotaClient, input: &WasmQueryEventsParams) -> PromisePaginatedEvents; + + #[wasm_bindgen(method, js_name = getCoins)] + pub fn get_coins(this: &WasmIotaClient, input: &WasmGetCoinsParams) -> PromisePaginatedCoins; +} + +// Helper struct used to convert TYPESCRIPT types to RUST types +#[derive(Clone)] +pub struct ManagedWasmIotaClient(WasmIotaClient); + +// convert TYPESCRIPT types to RUST types +impl ManagedWasmIotaClient { + pub fn new(iota_client: WasmIotaClient) -> Self { + ManagedWasmIotaClient(iota_client) + } + + pub async fn get_chain_identifier(&self) -> Result { + let promise: Promise = Promise::resolve(&WasmIotaClient::get_chain_identifier(&self.0)); + into_ts_sdk_result(JsFuture::from(promise).await) + } + + pub async fn execute_transaction_block( + &self, + tx_data_bcs: &TransactionDataBcs, + signatures: &[SignatureBcs], + options: Option, + request_type: Option, + ) -> IotaRpcResult { + let ex_tx_params: WasmExecuteTransactionBlockParams = serde_wasm_bindgen::to_value( + &ExecuteTransactionBlockParams::new(tx_data_bcs, signatures, options, request_type), + ) + .map_err(|e| { + console_log!( + "Error executing serde_wasm_bindgen::to_value(ExecuteTransactionBlockParams): {:?}", + e + ); + IotaRpcError::FfiError(format!("{:?}", e)) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::execute_transaction_block(&self.0, &ex_tx_params)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + Ok(IotaTransactionBlockResponseAdapter::new(result.into())) + } + + /** + * Return the dynamic field object information for a specified object + */ + pub async fn get_dynamic_field_object( + &self, + parent_object_id: ObjectID, + name: DynamicFieldName, + ) -> IotaRpcResult { + let params: WasmGetDynamicFieldObjectParams = + serde_wasm_bindgen::to_value(&GetDynamicFieldObjectParams::new(parent_object_id.to_string(), name)) + .map_err(|e| { + console_log!( + "Error executing serde_wasm_bindgen::to_value(WasmGetDynamicFieldObjectParams): {:?}", + e + ); + IotaRpcError::FfiError(format!("{:?}", e)) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::get_dynamic_field_object(&self.0, ¶ms)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + + Ok(result.into_serde()?) + } + + pub async fn get_object_with_options( + &self, + object_id: ObjectID, + options: IotaObjectDataOptions, + ) -> IotaRpcResult { + let params: WasmGetObjectParams = + serde_wasm_bindgen::to_value(&GetObjectParams::new(object_id.to_string(), Some(options))) + .map_err(|e| { + console_log!( + "Error executing serde_wasm_bindgen::to_value(WasmIotaObjectDataOptions): {:?}", + e + ); + IotaRpcError::FfiError(format!("{:?}", e)) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::get_object(&self.0, ¶ms)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + + Ok(result.into_serde()?) + } + + pub async fn get_owned_objects( + &self, + address: IotaAddress, + query: Option, + cursor: Option, + limit: Option, + ) -> IotaRpcResult { + let params: WasmGetOwnedObjectsParams = serde_wasm_bindgen::to_value(&GetOwnedObjectsParams::new( + address.to_string(), + cursor.map(|v| v.to_string()), + limit, + query.clone().map(|v| v.filter).flatten(), + query.clone().map(|v| v.options).flatten(), + )) + .map_err(|e| { + console_log!( + "Error executing serde_wasm_bindgen::to_value(WasmIotaObjectDataOptions): {:?}", + e + ); + IotaRpcError::FfiError(format!("{:?}", e)) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::get_owned_objects(&self.0, ¶ms)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + + Ok(result.into_serde()?) + } + + pub async fn get_transaction_with_options( + &self, + digest: TransactionDigest, + options: IotaTransactionBlockResponseOptions, + ) -> IotaRpcResult { + let params: WasmGetTransactionBlockParams = + serde_wasm_bindgen::to_value(&GetTransactionBlockParams::new(digest.to_string(), Some(options))) + .map_err(|e| { + console_log!( + "Error executing serde_wasm_bindgen::to_value(WasmIotaObjectDataOptions): {:?}", + e + ); + IotaRpcError::FfiError(format!("{:?}", e)) + })? + .into(); + + // Rust `ReadApi::get_transaction_with_options` calls `get_transaction_block` via http while + // TypeScript uses the name `getTransactionBlock` directly, so we have to call this function + let promise: Promise = Promise::resolve(&WasmIotaClient::get_transaction_block(&self.0, ¶ms)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + + Ok(IotaTransactionBlockResponseAdapter::new(result.into())) + } + + pub async fn get_reference_gas_price(&self) -> IotaRpcResult { + let promise: Promise = Promise::resolve(&WasmIotaClient::get_reference_gas_price(&self.0)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + + Ok(result.into_serde()?) + } + + pub async fn try_get_parsed_past_object( + &self, + object_id: ObjectID, + version: SequenceNumber, + options: IotaObjectDataOptions, + ) -> IotaRpcResult { + // TODO: does not work anymore, find out, why we need to pass a different `SequenceNumber` now + unimplemented!("try_get_parsed_past_object"); + // let params: WasmTryGetPastObjectParams = serde_wasm_bindgen::to_value(&TryGetPastObjectParams::new( + // object_id.to_string(), + // version, + // Some(options), + // )) + // .map_err(|e| { + // console_log!( + // "Error executing serde_wasm_bindgen::to_value(WasmIotaObjectDataOptions): {:?}", + // e + // ); + // IotaRpcError::FfiError(format!("{:?}", e)) + // })? + // .into(); + + // let promise: Promise = Promise::resolve(&WasmIotaClient::try_get_past_object(&self.0, ¶ms)); + // let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + // console_log!("Error executing JsFuture::from(promise): {:?}", e); + // IotaRpcError::FfiError(format!("{:?}", e)) + // })?; + + // Ok(result.into_serde()?) + } + + pub async fn query_events( + &self, + query: EventFilter, + cursor: Option, + limit: Option, + descending_order: bool, + ) -> IotaRpcResult { + let params: WasmQueryEventsParams = serde_wasm_bindgen::to_value(&QueryEventsParams::new( + query, + cursor, + limit, + Some(SortOrder::new(descending_order)), + )) + .map_err(|e| { + console_log!( + "Error executing serde_wasm_bindgen::to_value(WasmIotaObjectDataOptions): {:?}", + e + ); + IotaRpcError::FfiError(format!("{:?}", e)) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::query_events(&self.0, ¶ms)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + + Ok(result.into_serde()?) + } + + pub async fn get_coins( + &self, + owner: IotaAddress, + coin_type: Option, + cursor: Option, + limit: Option, + ) -> IotaRpcResult { + let params: WasmGetCoinsParams = serde_wasm_bindgen::to_value(&GetCoinsParams::new( + owner.to_string(), + coin_type.map(|v| v.to_string()), + cursor.map(|v| v.to_string()), + limit, + )) + .map_err(|e| { + console_log!( + "Error executing serde_wasm_bindgen::to_value(WasmIotaObjectDataOptions): {:?}", + e + ); + IotaRpcError::FfiError(format!("{:?}", e)) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::get_coins(&self.0, ¶ms)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + + Ok(result.into_serde()?) + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs new file mode 100644 index 0000000000..68d6b2277d --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -0,0 +1,241 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::execution_status::ExecutionStatus; +use identity_iota_interaction::ProgrammableTransactionBcs; +use js_sys::Promise; +use serde::Deserialize; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; +use wasm_bindgen_futures::JsFuture; + +use crate::bindings::WasmIotaClient; +use crate::common::into_sdk_type; +use crate::console_log; + +// TODO: fix/add +// not available anymore +// use crate::storage::WasmStorageSigner; +type WasmStorageSigner = (); +// not available anymore +// use identity_iota::iota::iota_sdk_abstraction::error::Error; +enum Error { + FfiError(String), +} + +#[wasm_bindgen(typescript_custom_section)] +const TS_SDK_TYPES: &'static str = r#" + import { + Balance, + ExecuteTransactionBlockParams, + GetCoinsParams, + GetDynamicFieldObjectParams, + GetObjectParams, + GetOwnedObjectsParams, + GetTransactionBlockParams, + IotaObjectData, + IotaObjectResponse, + IotaTransactionBlockResponse, + IotaTransactionBlockResponseOptions, + ObjectRead, + PaginatedCoins, + PaginatedEvents, + PaginatedObjectsResponse, + QueryEventsParams, + TryGetPastObjectParams, + } from "@iota/iota.js/client"; + import { bcs } from "@iota/iota.js/bcs"; + import { + executeTransaction, + IotaTransactionBlockResponseAdapter, + } from "./iota_client_helpers" + + // TODO: decide if we use this or replace it with an adapter written in TypeScript type if needed + type ProgrammableTransaction = ReturnType; +"#; + +#[wasm_bindgen(module = "@iota/iota.js/client")] +extern "C" { + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseBalance; + + #[wasm_bindgen(typescript_type = "IotaObjectData")] + pub type WasmIotaObjectData; + + #[wasm_bindgen(typescript_type = "ExecuteTransactionBlockParams")] + #[derive(Clone)] + pub type WasmExecuteTransactionBlockParams; + + #[wasm_bindgen(typescript_type = "IotaTransactionBlockResponseOptions")] + #[derive(Clone)] + pub type WasmIotaTransactionBlockResponseOptions; + + #[wasm_bindgen(typescript_type = "IotaTransactionBlockResponse")] + #[derive(Clone)] + pub type WasmIotaTransactionBlockResponse; + + #[wasm_bindgen(typescript_type = "GetDynamicFieldObjectParams")] + #[derive(Clone)] + pub type WasmGetDynamicFieldObjectParams; + + #[wasm_bindgen(typescript_type = "GetObjectParams")] + #[derive(Clone)] + pub type WasmGetObjectParams; + + #[wasm_bindgen(typescript_type = "Promise")] + #[derive(Clone)] + pub type PromiseIotaTransactionBlockResponse; + + #[wasm_bindgen(typescript_type = "Promise")] + #[derive(Clone)] + pub type PromiseIotaObjectResponse; + + #[wasm_bindgen(typescript_type = "GetOwnedObjectsParams")] + #[derive(Clone)] + pub type WasmGetOwnedObjectsParams; + + #[wasm_bindgen(typescript_type = "GetTransactionBlockParams")] + #[derive(Clone)] + pub type WasmGetTransactionBlockParams; + + #[wasm_bindgen(typescript_type = "Promise")] + #[derive(Clone)] + pub type PromisePaginatedObjectsResponse; + + #[wasm_bindgen(typescript_type = "TryGetPastObjectParams")] + #[derive(Clone)] + pub type WasmTryGetPastObjectParams; + + #[wasm_bindgen(typescript_type = "Promise")] + #[derive(Clone)] + pub type PromiseObjectRead; + + #[wasm_bindgen(typescript_type = "ExecutionStatus")] + #[derive(Clone)] + pub type WasmExecutionStatus; + + #[wasm_bindgen(typescript_type = "OwnedObjectRef")] + #[derive(Clone)] + pub type WasmOwnedObjectRef; + + #[wasm_bindgen(typescript_type = "QueryEventsParams")] + #[derive(Clone)] + pub type WasmQueryEventsParams; + + #[wasm_bindgen(typescript_type = "Promise")] + #[derive(Clone)] + pub type PromisePaginatedEvents; + + #[wasm_bindgen(typescript_type = "GetCoinsParams")] + #[derive(Clone)] + pub type WasmGetCoinsParams; + + #[wasm_bindgen(typescript_type = "Promise")] + #[derive(Clone)] + pub type PromisePaginatedCoins; + + #[wasm_bindgen(typescript_type = "Promise")] + #[derive(Clone)] + pub type PromiseIotaTransactionBlockResponseAdapter; +} + +#[wasm_bindgen(module = "/lib/iota_client_helpers.ts")] +extern "C" { + #[wasm_bindgen(typescript_type = "IotaTransactionBlockResponseAdapter")] + #[derive(Clone)] + pub type IotaTransactionBlockResponseAdapter; + + #[wasm_bindgen(constructor)] + pub fn new(response: WasmIotaTransactionBlockResponse) -> IotaTransactionBlockResponseAdapter; + + #[wasm_bindgen(method)] + pub fn effects_is_none(this: &IotaTransactionBlockResponseAdapter) -> bool; + + #[wasm_bindgen(method)] + pub fn effects_is_some(this: &IotaTransactionBlockResponseAdapter) -> bool; + + #[wasm_bindgen(method)] + pub fn to_string(this: &IotaTransactionBlockResponseAdapter) -> String; + + #[wasm_bindgen(method)] + fn effects_execution_status_inner(this: &IotaTransactionBlockResponseAdapter) -> Option; + + #[wasm_bindgen(method)] + fn effects_created_inner(this: &IotaTransactionBlockResponseAdapter) -> Option>; + + #[wasm_bindgen(js_name = executeTransaction)] + fn execute_transaction_inner( + iotaClient: &WasmIotaClient, // --> TypeScript: IotaClient + sender_address: String, // --> TypeScript: string + sender_public_key: Vec, // --> TypeScript: Uint8Array + tx_bcs: Vec, // --> TypeScript: Uint8Array, + signer: WasmStorageSigner, // --> TypeScript: Signer (iota_client_helpers module) + gas_budget: Option, // --> TypeScript: optional bigint + ) -> PromiseIotaTransactionBlockResponseAdapter; +} + +#[wasm_bindgen] // no module here, as imported via custom section above +extern "C" { + #[wasm_bindgen(typescript_type = "ProgrammableTransaction")] + pub type WasmProgrammableTransaction; +} + +#[derive(Deserialize)] +struct WasmExecutionStatusAdapter { + status: ExecutionStatus, +} + +impl IotaTransactionBlockResponseAdapter { + pub fn effects_execution_status(&self) -> Option { + self.effects_execution_status_inner().map(|s| { + let state: WasmExecutionStatusAdapter = + into_sdk_type(s).expect("[IotaTransactionBlockResponseAdapter] Failed to convert WasmExecutionStatus"); + state.status + }) + } + + pub fn effects_created(&self) -> Option> { + self.effects_created_inner().map(|vex_obj_ref| { + vex_obj_ref + .into_iter() + .map(|obj| { + into_sdk_type(obj).expect("[IotaTransactionBlockResponseAdapter] Failed to convert WasmOwnedObjectRef") + }) + .collect() + }) + } +} + +pub async fn execute_transaction( + iota_client: &WasmIotaClient, // --> Binding: WasmIotaClient + sender_address: IotaAddress, // --> Binding: String + sender_public_key: &[u8], // --> Binding: Vec + tx_bcs: ProgrammableTransactionBcs, // --> Binding: Vec + signer: WasmStorageSigner, // --> Binding: WasmStorageSigner + gas_budget: Option, // --> Binding: Option, +) -> Result { + let promise: Promise = Promise::resolve(&execute_transaction_inner( + iota_client, + sender_address.to_string(), + sender_public_key.to_vec(), + tx_bcs, + signer, + gas_budget, + )); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + Error::FfiError(format!("{:?}", e)) + })?; + + Ok(IotaTransactionBlockResponseAdapter::new(result.into())) +} + +#[derive(Deserialize)] +// TODO: add manual deserialization later on (must be deserializable, but WasmPT is not) +// pub struct ProgrammableTransaction(WasmProgrammableTransaction); +pub struct ProgrammableTransaction(()); + +// TODO: fill in required functions and data handling here +impl ProgrammableTransaction {} diff --git a/bindings/wasm/iota_interaction_ts/src/common/macros.rs b/bindings/wasm/iota_interaction_ts/src/common/macros.rs new file mode 100644 index 0000000000..259fc54171 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/common/macros.rs @@ -0,0 +1,62 @@ +// Copyright 2020-2021 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use wasm_bindgen::prelude::wasm_bindgen; + +#[macro_export] +macro_rules! log { + ($($tt:tt)*) => { + web_sys::console::log_1(&format!($($tt)*).into()); + } +} + +/// Log to console utility without the need for web_sys dependency +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console, js_name = log)] + pub fn console_log(s: &str); +} + +/// Logging macro without the need for web_sys dependency +#[macro_export] +macro_rules! console_log { + ($($tt:tt)*) => { + crate::common::macros::console_log((format!($($tt)*)).as_str()) + } +} + +#[macro_export] +macro_rules! impl_wasm_clone { + ($wasm_class:ident, $js_class:ident) => { + #[wasm_bindgen(js_class = $js_class)] + impl $wasm_class { + /// Deep clones the object. + #[wasm_bindgen(js_name = clone)] + pub fn deep_clone(&self) -> $wasm_class { + return $wasm_class(self.0.clone()); + } + } + }; +} + +#[macro_export] +macro_rules! impl_wasm_json { + ($wasm_class:ident, $js_class:ident) => { + #[wasm_bindgen(js_class = $js_class)] + impl $wasm_class { + /// Serializes this to a JSON object. + #[wasm_bindgen(js_name = toJSON)] + pub fn to_json(&self) -> $crate::error::Result { + use $crate::error::WasmResult; + JsValue::from_serde(&self.0).wasm_result() + } + + /// Deserializes an instance from a JSON object. + #[wasm_bindgen(js_name = fromJSON)] + pub fn from_json(json: &JsValue) -> $crate::error::Result<$wasm_class> { + use $crate::error::WasmResult; + json.into_serde().map(Self).wasm_result() + } + } + }; +} diff --git a/bindings/wasm/iota_interaction_ts/src/common/mod.rs b/bindings/wasm/iota_interaction_ts/src/common/mod.rs new file mode 100644 index 0000000000..f764977e2d --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/common/mod.rs @@ -0,0 +1,11 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +pub mod macros; + +pub mod types; +pub mod utils; + +pub use types::*; +pub use utils::*; diff --git a/bindings/wasm/iota_interaction_ts/src/common/types.rs b/bindings/wasm/iota_interaction_ts/src/common/types.rs new file mode 100644 index 0000000000..a3197dafe6 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/common/types.rs @@ -0,0 +1,85 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_core::common::Object; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; + +use crate::error::WasmResult; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseVoid; + + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseBigint; + + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseBool; + + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseString; + + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseOptionString; + + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseUint8Array; + + #[wasm_bindgen(typescript_type = "Array")] + pub type ArrayString; + + #[wasm_bindgen(typescript_type = "Map")] + pub type MapStringAny; + + #[wasm_bindgen(typescript_type = "Record")] + pub type RecordStringAny; + + #[wasm_bindgen(typescript_type = "number | number[]")] + pub type UOneOrManyNumber; + + #[wasm_bindgen(typescript_type = "string | string[] | null")] + pub type OptionOneOrManyString; + + #[wasm_bindgen(typescript_type = "VerificationMethod[]")] + pub type ArrayVerificationMethod; + + #[wasm_bindgen(typescript_type = "Array")] + pub type ArrayCoreMethodRef; + + #[wasm_bindgen(typescript_type = "DIDUrl | string")] + pub type UDIDUrlQuery; + + #[wasm_bindgen(typescript_type = "Service[]")] + pub type ArrayService; +} + +impl TryFrom for MapStringAny { + type Error = JsValue; + + fn try_from(properties: Object) -> Result { + MapStringAny::try_from(&properties) + } +} + +impl TryFrom<&Object> for MapStringAny { + type Error = JsValue; + + fn try_from(properties: &Object) -> Result { + let map: js_sys::Map = js_sys::Map::new(); + for (key, value) in properties.iter() { + map.set( + &JsValue::from_str(key.as_str()), + &JsValue::from_serde(&value).wasm_result()?, + ); + } + Ok(map.unchecked_into::()) + } +} + +impl Default for MapStringAny { + fn default() -> Self { + js_sys::Map::new().unchecked_into() + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/common/utils.rs b/bindings/wasm/iota_interaction_ts/src/common/utils.rs new file mode 100644 index 0000000000..5b2d698be7 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/common/utils.rs @@ -0,0 +1,27 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::de::DeserializeOwned; +use wasm_bindgen::prelude::*; + +use crate::error::wasm_error; +use crate::error::WasmError; + +pub fn into_sdk_type<'a, T: DeserializeOwned, W: Into>( + wasm_type_instance: W, +) -> core::result::Result> { + let js_value: JsValue = wasm_type_instance.into(); + match serde_wasm_bindgen::from_value::(js_value.clone()) { + Ok(ret_val) => Ok(ret_val), + Err(e) => { + // TODO: Replace all console_log! usages by proper Error management and Result types. + // Use console_log! only for debug purposes + console_log!( + "[iota_interaction_ts::common::utils - fn into_sdk_type]\n js_value: {:?}\n Error: {:?}", + js_value, + e + ); + Err(e.into()) + } + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/error.rs b/bindings/wasm/iota_interaction_ts/src/error.rs new file mode 100644 index 0000000000..2a09969148 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/error.rs @@ -0,0 +1,212 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::result::Result as StdResult; + +use serde::de::DeserializeOwned; +use std::borrow::Cow; +use std::fmt::Debug; +use std::fmt::Display; +use wasm_bindgen::JsValue; + +use crate::common::into_sdk_type; +use identity_iota_interaction::types::execution_status::CommandArgumentError; +use identity_iota_interaction::types::execution_status::ExecutionFailureStatus; +use identity_iota_interaction::types::execution_status::PackageUpgradeError; +use identity_iota_interaction::types::execution_status::TypeArgumentError; +use thiserror::Error as ThisError; + +/// Convenience wrapper for `Result`. +/// +/// All exported errors must be converted to [`JsValue`] when using wasm_bindgen. +/// See: https://rustwasm.github.io/docs/wasm-bindgen/reference/types/result.html +pub type Result = core::result::Result; + +/// Convert an error into an idiomatic [js_sys::Error]. +pub fn wasm_error<'a, E>(error: E) -> JsValue +where + E: Into>, +{ + let wasm_err: WasmError<'_> = error.into(); + JsValue::from(wasm_err) +} + +/// Convenience trait to simplify `result.map_err(wasm_error)` to `result.wasm_result()` +pub trait WasmResult { + fn wasm_result(self) -> Result; +} + +impl<'a, T, E> WasmResult for core::result::Result +where + E: Into>, +{ + fn wasm_result(self) -> Result { + self.map_err(wasm_error) + } +} + +/// Convenience struct to convert internal errors to [js_sys::Error]. Uses [std::borrow::Cow] +/// internally to avoid unnecessary clones. +/// +/// This is a workaround for orphan rules so we can implement [core::convert::From] on errors from +/// dependencies. +#[derive(Debug, Clone)] +pub struct WasmError<'a> { + pub name: Cow<'a, str>, + pub message: Cow<'a, str>, +} + +impl<'a> WasmError<'a> { + pub fn new(name: Cow<'a, str>, message: Cow<'a, str>) -> Self { + Self { name, message } + } +} + +/// Convert [WasmError] into [js_sys::Error] for idiomatic error handling. +impl From> for js_sys::Error { + fn from(error: WasmError<'_>) -> Self { + let js_error = js_sys::Error::new(&error.message); + js_error.set_name(&error.name); + js_error + } +} + +/// Convert [WasmError] into [wasm_bindgen::JsValue]. +impl From> for JsValue { + fn from(error: WasmError<'_>) -> Self { + JsValue::from(js_sys::Error::from(error)) + } +} + +/// Implement WasmError for each type individually rather than a trait due to Rust's orphan rules. +/// Each type must implement `Into<&'static str> + Display`. The `Into<&'static str>` trait can be +/// derived using `strum::IntoStaticStr`. +#[macro_export] +macro_rules! impl_wasm_error_from { + ( $($t:ty),* ) => { + $(impl From<$t> for WasmError<'_> { + fn from(error: $t) -> Self { + Self { + message: Cow::Owned(ErrorMessage(&error).to_string()), + name: Cow::Borrowed(error.into()), + } + } + })* + } +} + +// Similar to `impl_wasm_error_from`, but uses the types name instead of requiring/calling Into &'static str +#[macro_export] +macro_rules! impl_wasm_error_from_with_struct_name { + ( $($t:ty),* ) => { + $(impl From<$t> for WasmError<'_> { + fn from(error: $t) -> Self { + Self { + message: Cow::Owned(error.to_string()), + name: Cow::Borrowed(stringify!($t)), + } + } + })* + } +} + +impl From for WasmError<'_> { + fn from(error: JsValue) -> Self { + let js_err = js_sys::Error::from(error); + let name: String = js_err.name().into(); + let message: String = js_err.message().into(); + WasmError::new(name.into(), message.into()) + } +} + +// identity_iota::iota now has some errors where the error message does not include the source error's error message. +// This is in compliance with the Rust error handling project group's recommendation: +// * An error type with a source error should either return that error via source or include that source's error message +// in its own Display output, but never both. * +// See https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html#guidelines-for-implementing-displayfmt-and-errorsource. +// +// However in WasmError we want the display message of the entire error chain. We introduce a workaround here that let's +// us display the entire display chain for new variants that don't include the error message of the source error in its +// own display. + +// the following function is inspired by https://www.lpalmieri.com/posts/error-handling-rust/#error-source +fn error_chain_fmt(e: &impl std::error::Error, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{e}. ")?; + let mut current = e.source(); + while let Some(cause) = current { + write!(f, "Caused by: {cause}. ")?; + current = cause.source(); + } + Ok(()) +} + +struct ErrorMessage<'a, E: std::error::Error>(&'a E); + +impl<'a, E: std::error::Error> Display for ErrorMessage<'a, E> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + error_chain_fmt(self.0, f) + } +} + +impl From for WasmError<'_> { + fn from(error: serde_json::Error) -> Self { + Self { + name: Cow::Borrowed("serde_json::Error"), // the exact error code is embedded in the message + message: Cow::Owned(error.to_string()), + } + } +} + +impl From for WasmError<'_> { + fn from(error: serde_wasm_bindgen::Error) -> Self { + Self { + name: Cow::Borrowed("serde_wasm_bindgen::Error"), + message: Cow::Owned(ErrorMessage(&error).to_string()), + } + } +} + +/// Consumes the struct and returns a Result<_, String>, leaving an `Ok` value untouched. +pub fn stringify_js_error(result: Result) -> StdResult { + result.map_err(|js_value| { + let error_string: String = match wasm_bindgen::JsCast::dyn_into::(js_value) { + Ok(js_err) => ToString::to_string(&js_err.to_string()), + Err(js_val) => { + // Fall back to debug formatting if this is not a proper JS Error instance. + format!("{js_val:?}") + } + }; + error_string + }) +} + +#[derive(ThisError, Debug)] +pub enum TsSdkError { + #[error("[TsSdkError] PackageUpgradeError: {0}")] + PackageUpgradeError(PackageUpgradeError), + #[error("[TsSdkError] CommandArgumentError: {0}")] + CommandArgumentError(CommandArgumentError), + #[error("[TsSdkError] ExecutionFailureStatus: {0}")] + ExecutionFailureStatus(ExecutionFailureStatus), + #[error("[TsSdkError] TypeArgumentError: {0}")] + TypeArgumentError(TypeArgumentError), + #[error("[TsSdkError] WasmError:{{\n name: {0},\n message: {1}\n}}")] + WasmError(String, String), + #[error("[TsSdkError] JsSysError: {0}")] + JsSysError(String), +} + +pub type TsSdkResult = core::result::Result; + +impl From> for TsSdkError { + fn from(err: WasmError<'_>) -> Self { + TsSdkError::WasmError(err.name.to_string(), err.message.to_string()) + } +} + +pub fn into_ts_sdk_result(result: Result) -> TsSdkResult { + let result_str = stringify_js_error(result); + let js_value = result_str.map_err(|e| TsSdkError::JsSysError(e))?; + let ret_val: T = into_sdk_type(js_value)?; + Ok(ret_val) +} diff --git a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs new file mode 100644 index 0000000000..166e818ebc --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs @@ -0,0 +1,229 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; +use std::collections::HashSet; + +use super::TransactionBuilderAdapter; +use crate::error::TsSdkError; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::base_types::SequenceNumber; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::BorrowIntentFnInternalT; +use identity_iota_interaction::ControllerIntentFnInternalT; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::MoveType; +use identity_iota_interaction::ProgrammableTransactionBcs; + +pub struct IdentityMoveCallsTsSdk {} + +impl IdentityMoveCalls for IdentityMoveCallsTsSdk { + type Error = TsSdkError; + type NativeTxBuilder = (); // TODO: Set this to the wasm32... type that is wrapped by IdentityMoveCallsTsSdk + + fn propose_borrow( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + expiration: Option, + package_id: ObjectID, + ) -> Result { + todo!() + } + + fn execute_borrow>( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + objects: Vec, + intent_fn: F, + package: ObjectID, + ) -> Result { + todo!() + } + + fn create_and_execute_borrow>( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + intent_fn: F, + expiration: Option, + package_id: ObjectID, + ) -> anyhow::Result { + todo!() + } + + fn propose_config_change( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + expiration: Option, + threshold: Option, + controllers_to_add: I1, + controllers_to_remove: HashSet, + controllers_to_update: I2, + package: ObjectID, + ) -> Result + where + I1: IntoIterator, + I2: IntoIterator, + { + unimplemented!(); + } + + fn execute_config_change( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + proposal_id: ObjectID, + package: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn propose_controller_execution( + identity: OwnedObjectRef, + capability: ObjectRef, + controller_cap_id: ObjectID, + expiration: Option, + package_id: ObjectID, + ) -> Result { + todo!() + } + + fn execute_controller_execution>( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + borrowing_controller_cap_ref: ObjectRef, + intent_fn: F, + package: ObjectID, + ) -> Result { + todo!() + } + + fn create_and_execute_controller_execution( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + borrowing_controller_cap_ref: ObjectRef, + intent_fn: F, + package_id: ObjectID, + ) -> Result + where + F: ControllerIntentFnInternalT, + { + todo!() + } + + fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { + unimplemented!(); + } + + fn new_with_controllers( + did_doc: &[u8], + controllers: C, + threshold: u64, + package_id: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn propose_deactivation( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + package_id: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn execute_deactivation( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn approve_proposal( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + proposal_id: ObjectID, + package: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn propose_send( + identity: OwnedObjectRef, + capability: ObjectRef, + transfer_map: Vec<(ObjectID, IotaAddress)>, + expiration: Option, + package_id: ObjectID, + ) -> Result { + todo!() + } + + fn create_and_execute_send( + identity: OwnedObjectRef, + capability: ObjectRef, + transfer_map: Vec<(ObjectID, IotaAddress)>, + expiration: Option, + objects: Vec<(ObjectRef, TypeTag)>, + package: ObjectID, + ) -> anyhow::Result { + todo!() + } + + fn execute_send( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + objects: Vec<(ObjectRef, TypeTag)>, + package: ObjectID, + ) -> Result { + todo!() + } + + fn propose_update( + identity: OwnedObjectRef, + capability: ObjectRef, + did_doc: impl AsRef<[u8]>, + expiration: Option, + package_id: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn execute_update( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result { + unimplemented!(); + } + + fn propose_upgrade( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + package_id: ObjectID, + ) -> Result { + todo!() + } + + fn execute_upgrade( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result { + todo!() + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs new file mode 100644 index 0000000000..f408665516 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs @@ -0,0 +1,382 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::boxed::Box; +use std::option::Option; +use std::result::Result; + +use identity_iota_interaction::types::digests::TransactionDigest; +use identity_iota_interaction::types::dynamic_field::DynamicFieldName; +use secret_storage::Signer; + +use identity_iota_interaction::error::IotaRpcResult; +use identity_iota_interaction::rpc_types::CoinPage; +use identity_iota_interaction::rpc_types::EventFilter; +use identity_iota_interaction::rpc_types::EventPage; +use identity_iota_interaction::rpc_types::IotaExecutionStatus; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::IotaObjectDataOptions; +use identity_iota_interaction::rpc_types::IotaObjectResponse; +use identity_iota_interaction::rpc_types::IotaObjectResponseQuery; +use identity_iota_interaction::rpc_types::IotaPastObjectResponse; +use identity_iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; +use identity_iota_interaction::rpc_types::ObjectsPage; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::SequenceNumber; +use identity_iota_interaction::types::event::EventID; +use identity_iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; +use identity_iota_interaction::CoinReadTrait; +use identity_iota_interaction::EventTrait; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseBcs; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::ProgrammableTransactionBcs; +use identity_iota_interaction::QuorumDriverTrait; +use identity_iota_interaction::ReadTrait; +use identity_iota_interaction::SignatureBcs; +use identity_iota_interaction::TransactionDataBcs; + +use crate::bindings::IotaTransactionBlockResponseAdapter; +use crate::bindings::ManagedWasmIotaClient; +use crate::bindings::WasmIotaClient; +use crate::error::TsSdkError; + +#[allow(dead_code)] +pub trait IotaTransactionBlockResponseAdaptedT: + IotaTransactionBlockResponseT +{ +} +impl IotaTransactionBlockResponseAdaptedT for T where + T: IotaTransactionBlockResponseT +{ +} +#[allow(dead_code)] +pub type IotaTransactionBlockResponseAdaptedTraitObj = + Box>; + +#[allow(dead_code)] +pub trait QuorumDriverApiAdaptedT: + QuorumDriverTrait +{ +} +impl QuorumDriverApiAdaptedT for T where + T: QuorumDriverTrait +{ +} +#[allow(dead_code)] +pub type QuorumDriverApiAdaptedTraitObj = + Box>; + +#[allow(dead_code)] +pub trait ReadApiAdaptedT: ReadTrait {} +impl ReadApiAdaptedT for T where + T: ReadTrait +{ +} +#[allow(dead_code)] +pub type ReadApiAdaptedTraitObj = + Box>; + +#[allow(dead_code)] +pub trait CoinReadApiAdaptedT: CoinReadTrait {} +impl CoinReadApiAdaptedT for T where T: CoinReadTrait {} +#[allow(dead_code)] +pub type CoinReadApiAdaptedTraitObj = Box>; + +#[allow(dead_code)] +pub trait EventApiAdaptedT: EventTrait {} +impl EventApiAdaptedT for T where T: EventTrait {} +#[allow(dead_code)] +pub type EventApiAdaptedTraitObj = Box>; + +#[allow(dead_code)] +pub trait IotaClientAdaptedT: + IotaClientTrait +{ +} +impl IotaClientAdaptedT for T where + T: IotaClientTrait +{ +} +#[allow(dead_code)] +pub type IotaClientAdaptedTraitObj = + Box>; + +pub struct IotaTransactionBlockResponseProvider { + response: IotaTransactionBlockResponseAdapter, +} + +impl IotaTransactionBlockResponseProvider { + pub fn new(response: IotaTransactionBlockResponseAdapter) -> Self { + IotaTransactionBlockResponseProvider { response } + } +} + +#[async_trait::async_trait(?Send)] +impl IotaTransactionBlockResponseT for IotaTransactionBlockResponseProvider { + type Error = TsSdkError; + type NativeResponse = IotaTransactionBlockResponseAdapter; + + fn effects_is_none(&self) -> bool { + self.response.effects_is_none() + } + + fn effects_is_some(&self) -> bool { + self.response.effects_is_some() + } + + fn to_string(&self) -> String { + format!("{:?}", self.response.to_string()) + } + + fn effects_execution_status(&self) -> Option { + self + .response + .effects_execution_status() + .map(|wasm_status| wasm_status.into()) + } + + fn effects_created(&self) -> Option> { + self + .response + .effects_created() + .map(|wasm_o_ref_vec| wasm_o_ref_vec.into()) + } + + fn as_native_response(&self) -> &Self::NativeResponse { + &self.response + } + + fn as_mut_native_response(&mut self) -> &mut Self::NativeResponse { + &mut self.response + } + + fn clone_native_response(&self) -> Self::NativeResponse { + self.response.clone() + } +} + +pub struct ReadAdapter { + client: ManagedWasmIotaClient, +} + +#[async_trait::async_trait(?Send)] +impl ReadTrait for ReadAdapter { + type Error = TsSdkError; + type NativeResponse = IotaTransactionBlockResponseAdapter; + + async fn get_chain_identifier(&self) -> Result { + Ok(self.client.get_chain_identifier().await.unwrap()) + } + + async fn get_dynamic_field_object( + &self, + parent_object_id: ObjectID, + name: DynamicFieldName, + ) -> IotaRpcResult { + self.client.get_dynamic_field_object(parent_object_id, name).await + } + + async fn get_object_with_options( + &self, + object_id: ObjectID, + options: IotaObjectDataOptions, + ) -> IotaRpcResult { + self.client.get_object_with_options(object_id, options).await + } + + async fn get_owned_objects( + &self, + address: IotaAddress, + query: Option, + cursor: Option, + limit: Option, + ) -> IotaRpcResult { + self.client.get_owned_objects(address, query, cursor, limit).await + } + + async fn get_reference_gas_price(&self) -> IotaRpcResult { + self.client.get_reference_gas_price().await + } + + async fn get_transaction_with_options( + &self, + digest: TransactionDigest, + options: IotaTransactionBlockResponseOptions, + ) -> IotaRpcResult { + let wasm_response = self.client.get_transaction_with_options(digest, options).await?; + + Ok(Box::new(IotaTransactionBlockResponseProvider::new(wasm_response))) + } + + async fn try_get_parsed_past_object( + &self, + object_id: ObjectID, + version: SequenceNumber, + options: IotaObjectDataOptions, + ) -> IotaRpcResult { + // TODO: does not work anymore, find out, why we need to pass a different `SequenceNumber` now + unimplemented!("try_get_parsed_past_object"); + // self + // .client + // .try_get_parsed_past_object(object_id, version, options) + // .await + } +} + +pub struct QuorumDriverAdapter { + client: ManagedWasmIotaClient, +} + +#[async_trait::async_trait(?Send)] +impl QuorumDriverTrait for QuorumDriverAdapter { + type Error = TsSdkError; + type NativeResponse = IotaTransactionBlockResponseAdapter; + + async fn execute_transaction_block( + &self, + tx_data_bcs: &TransactionDataBcs, + signatures: &[SignatureBcs], + options: Option, + request_type: Option, + ) -> IotaRpcResult { + let wasm_response = self + .client + .execute_transaction_block(tx_data_bcs, signatures, options, request_type) + .await?; + Ok(Box::new(IotaTransactionBlockResponseProvider::new(wasm_response))) + } +} + +pub struct EventAdapter { + client: ManagedWasmIotaClient, +} + +#[async_trait::async_trait(?Send)] +impl EventTrait for EventAdapter { + type Error = TsSdkError; + + async fn query_events( + &self, + query: EventFilter, + cursor: Option, + limit: Option, + descending_order: bool, + ) -> IotaRpcResult { + self.client.query_events(query, cursor, limit, descending_order).await + } +} + +pub struct CoinReadAdapter { + client: ManagedWasmIotaClient, +} + +#[async_trait::async_trait(?Send)] +impl CoinReadTrait for CoinReadAdapter { + type Error = TsSdkError; + + async fn get_coins( + &self, + owner: IotaAddress, + coin_type: Option, + cursor: Option, + limit: Option, + ) -> IotaRpcResult { + self.client.get_coins(owner, coin_type, cursor, limit).await + } +} + +#[derive(Clone)] +pub struct IotaClientTsSdk { + iota_client: ManagedWasmIotaClient, +} + +#[async_trait::async_trait(?Send)] +impl IotaClientTrait for IotaClientTsSdk { + type Error = TsSdkError; + type NativeResponse = IotaTransactionBlockResponseAdapter; + + fn quorum_driver_api(&self) -> QuorumDriverApiAdaptedTraitObj { + Box::new(QuorumDriverAdapter { + client: self.iota_client.clone(), + }) + } + + fn read_api(&self) -> ReadApiAdaptedTraitObj { + Box::new(ReadAdapter { + client: self.iota_client.clone(), + }) + } + + fn coin_read_api(&self) -> Box + '_> { + Box::new(CoinReadAdapter { + client: self.iota_client.clone(), + }) + } + + fn event_api(&self) -> Box + '_> { + Box::new(EventAdapter { + client: self.iota_client.clone(), + }) + } + + async fn execute_transaction>( + &self, + sender_address: IotaAddress, + sender_public_key: &[u8], + tx_bcs: ProgrammableTransactionBcs, + gas_budget: Option, + signer: &S, + ) -> Result { + unimplemented!(); + // let wasm_response = execute_transaction( + // &self.iota_client.clone().0, + // sender_address, + // sender_public_key, + // tx_bcs, + // *signer.clone(), + // gas_budget, + // ) + // .await?; + // Ok(Box::new(IotaTransactionBlockResponseProvider::new(wasm_response))) + } + + async fn default_gas_budget( + &self, + sender_address: IotaAddress, + tx_bcs: &ProgrammableTransactionBcs, + ) -> Result { + unimplemented!(); + } + + async fn get_previous_version(&self, iod: IotaObjectData) -> Result, Self::Error> { + unimplemented!(); + } + + async fn get_past_object( + &self, + object_id: ObjectID, + version: SequenceNumber, + ) -> Result { + self + .iota_client + .try_get_parsed_past_object(object_id, version, IotaObjectDataOptions::full_content()) + .await + .map_err(|err| { + // TODO: check error variant here, selection has been reduced / focused + // Self::Error::InvalidIdentityHistory(format!("could not look up object {object_id} version {version}; {err}")) + Self::Error::JsSysError(format!("could not look up object {object_id} version {version}; {err}")) + }) + } +} + +impl IotaClientTsSdk { + pub fn new(iota_client: WasmIotaClient) -> Result { + Ok(Self { + iota_client: ManagedWasmIotaClient::new(iota_client), + }) + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/lib.rs b/bindings/wasm/iota_interaction_ts/src/lib.rs new file mode 100644 index 0000000000..7418e74c3b --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/lib.rs @@ -0,0 +1,57 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(target_arch = "wasm32")] +pub mod bindings; + +#[cfg(target_arch = "wasm32")] +pub mod asset_move_calls; +#[cfg(target_arch = "wasm32")] +pub mod common; +#[cfg(target_arch = "wasm32")] +pub mod error; +#[cfg(target_arch = "wasm32")] +pub mod identity_move_calls; +#[cfg(target_arch = "wasm32")] +pub mod iota_client_ts_sdk; +#[cfg(target_arch = "wasm32")] +mod migration_move_calls; +#[cfg(target_arch = "wasm32")] +pub mod transaction_builder; + +#[cfg(target_arch = "wasm32")] +pub use asset_move_calls::AssetMoveCallsTsSdk as AssetMoveCallsAdapter; +#[cfg(target_arch = "wasm32")] +pub use identity_move_calls::IdentityMoveCallsTsSdk as IdentityMoveCallsAdapter; +#[cfg(target_arch = "wasm32")] +pub use iota_client_ts_sdk::IotaClientTsSdk as IotaClientAdapter; +#[cfg(target_arch = "wasm32")] +pub use iota_client_ts_sdk::IotaTransactionBlockResponseProvider as IotaTransactionBlockResponseAdapter; +#[cfg(target_arch = "wasm32")] +pub use migration_move_calls::MigrationMoveCallsTsSdk as MigrationMoveCallsAdapter; +#[cfg(target_arch = "wasm32")] +pub use transaction_builder::TransactionBuilderTsSdk as TransactionBuilderAdapter; + +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaTransactionBlockResponseAdaptedT; + #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaTransactionBlockResponseAdaptedTraitObj; + #[allow(unused_imports)] pub use iota_client_ts_sdk::QuorumDriverApiAdaptedT; + #[allow(unused_imports)] pub use iota_client_ts_sdk::QuorumDriverApiAdaptedTraitObj; + #[allow(unused_imports)] pub use iota_client_ts_sdk::ReadApiAdaptedT; + #[allow(unused_imports)] pub use iota_client_ts_sdk::ReadApiAdaptedTraitObj; + #[allow(unused_imports)] pub use iota_client_ts_sdk::CoinReadApiAdaptedT; + #[allow(unused_imports)] pub use iota_client_ts_sdk::CoinReadApiAdaptedTraitObj; + #[allow(unused_imports)] pub use iota_client_ts_sdk::EventApiAdaptedT; + #[allow(unused_imports)] pub use iota_client_ts_sdk::EventApiAdaptedTraitObj; + #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaClientAdaptedT; + #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaClientAdaptedTraitObj; + + #[allow(unused_imports)] pub use error::TsSdkError as AdapterError; + #[allow(unused_imports)] pub use bindings::IotaTransactionBlockResponseAdapter as AdapterNativeResponse; + + #[allow(unused_imports)] pub use transaction_builder::NativeTsTransactionBuilderBindingWrapper; + + #[allow(unused_imports)] pub use bindings::ProgrammableTransaction; + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs new file mode 100644 index 0000000000..cea88d5026 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs @@ -0,0 +1,28 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota_interaction::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use identity_iota_interaction::MigrationMoveCalls; +use identity_iota_interaction::ProgrammableTransactionBcs; + +use crate::error::TsSdkError; + +pub struct MigrationMoveCallsTsSdk {} + +impl MigrationMoveCalls for MigrationMoveCallsTsSdk { + type Error = TsSdkError; + + fn migrate_did_output( + did_output: ObjectRef, + creation_timestamp: Option, + migration_registry: OwnedObjectRef, + package: ObjectID, + ) -> anyhow::Result { + unimplemented!(); + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/transaction_builder.rs b/bindings/wasm/iota_interaction_ts/src/transaction_builder.rs new file mode 100644 index 0000000000..ad71a347d5 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/transaction_builder.rs @@ -0,0 +1,62 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::Deref; +use std::ops::DerefMut; + +use crate::error::TsSdkError; +use identity_iota_interaction::ProgrammableTransactionBcs; +use identity_iota_interaction::TransactionBuilderT; + +// TODO: When the rust type wrapping the native TS transaction-builder has been +// developed, replace the NativeTsTransactionBuilderBindingWrapper type +// with the final type name (NativeTsTransactionBuilderBindingWrapper is +// also imported in identity_iota_core/src/rebased/...) +pub type NativeTsTransactionBuilderBindingWrapper = (); + +pub struct TransactionBuilderTsSdk { + pub(crate) builder: NativeTsTransactionBuilderBindingWrapper, +} + +impl TransactionBuilderTsSdk { + pub fn new(builder: NativeTsTransactionBuilderBindingWrapper) -> Self { + TransactionBuilderTsSdk { builder } + } +} + +impl TransactionBuilderT for TransactionBuilderTsSdk { + type Error = TsSdkError; + type NativeTxBuilder = NativeTsTransactionBuilderBindingWrapper; + + fn finish(self) -> Result { + unimplemented!(); + } + + fn as_native_tx_builder(&mut self) -> &mut Self::NativeTxBuilder { + todo!() + } + + fn into_native_tx_builder(self) -> Self::NativeTxBuilder { + todo!() + } +} + +impl Default for TransactionBuilderTsSdk { + fn default() -> Self { + unimplemented!(); + } +} + +impl Deref for TransactionBuilderTsSdk { + type Target = NativeTsTransactionBuilderBindingWrapper; + + fn deref(&self) -> &Self::Target { + &self.builder + } +} + +impl DerefMut for TransactionBuilderTsSdk { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.builder + } +} diff --git a/bindings/wasm/iota_interaction_ts/tsconfig.json b/bindings/wasm/iota_interaction_ts/tsconfig.json new file mode 100644 index 0000000000..8b414bfdf9 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../tsconfig.json", + "entryPoints": [ + "./node/" + ], + "tsconfig": "./tsconfig.typedoc.json", + "out": "./docs/wasm", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@iota/iota-interaction-ts/*": [ + "./*" + ], + "@iota/identity-wasm/*": [ + "../identity_wasm/*" + ] + } + } +} diff --git a/bindings/wasm/iota_interaction_ts/tsconfig.node.json b/bindings/wasm/iota_interaction_ts/tsconfig.node.json new file mode 100644 index 0000000000..c75065fb27 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/tsconfig.node.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "ES2020", + "esModuleInterop": true, + "module": "commonjs" + } +} \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/tsconfig.typedoc.json b/bindings/wasm/iota_interaction_ts/tsconfig.typedoc.json new file mode 100644 index 0000000000..bfc43be938 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/tsconfig.typedoc.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.node.json", + "include": [ + "node/**/*" + ] +} diff --git a/bindings/wasm/iota_interaction_ts/typedoc.json b/bindings/wasm/iota_interaction_ts/typedoc.json new file mode 100644 index 0000000000..0f49cbb8b0 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/typedoc.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "../typedoc.json" + ] +} \ No newline at end of file diff --git a/bindings/wasm/lib/tsconfig.json b/bindings/wasm/lib/tsconfig.json deleted file mode 100644 index f656dc1cc7..0000000000 --- a/bindings/wasm/lib/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.node.json", - "compilerOptions": { - "baseUrl": "./", - "paths": { - "~identity_wasm": ["../node/identity_wasm", "./identity_wasm.js"], - "~sdk-wasm": ["../node_modules/@iota/sdk-wasm/node", "@iota/sdk-wasm/node"], - "../lib": ["."] - }, - "outDir": "../node", - "declarationDir": "../node" - } -} diff --git a/bindings/wasm/lib/tsconfig.web.json b/bindings/wasm/lib/tsconfig.web.json deleted file mode 100644 index 7216ccd75c..0000000000 --- a/bindings/wasm/lib/tsconfig.web.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": "./", - "paths": { - "~identity_wasm": ["../web/identity_wasm", "./identity_wasm.js"], - "~sdk-wasm": ["../node_modules/@iota/sdk-wasm/web", "@iota/sdk-wasm/web"], - "../lib": ["."] - }, - "outDir": "../web", - "declarationDir": "../web", - "module": "ES2020" - } -} diff --git a/bindings/wasm/tsconfig.json b/bindings/wasm/tsconfig.json index ae8ae8a323..27f854938a 100644 --- a/bindings/wasm/tsconfig.json +++ b/bindings/wasm/tsconfig.json @@ -1,17 +1,38 @@ { - "compilerOptions": { - "baseUrl": ".", - "lib": ["ES2020", "DOM"], - "declaration": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "node", - "noImplicitAny": true, - "preserveConstEnums": true, - "forceConsistentCasingInFileNames": true, - "paths": { - "@iota/identity-wasm/*": ["./*"] - } - }, - "exclude": ["node_modules"] + "$schema": "https://typedoc.org/schema.json", + "disableSources": true, + "excludePrivate": true, + "excludeInternal": true, + "excludeNotDocumented": true, + "excludeExternals": true, + "entryPointStrategy": "expand", + "plugin": [ + "typedoc-plugin-markdown" + ], + "readme": "none", + "githubPages": false, + "theme": "markdown", + "entryFileName": "api_ref.md", + "hideBreadcrumbs": true, + "hideGenerator": true, + "sort": [ + "source-order" + ], + "compilerOptions": { + "lib": [ + "ES2020", + "DOM" + ], + "declaration": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "node", + "noImplicitAny": true, + "preserveConstEnums": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + }, + "exclude": [ + "node_modules" + ] } diff --git a/bindings/wasm/tsconfig.node.json b/bindings/wasm/tsconfig.node.json deleted file mode 100644 index 6e8349baee..0000000000 --- a/bindings/wasm/tsconfig.node.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "target": "ES2020", - "esModuleInterop": true, - "module": "commonjs" - } -} diff --git a/bindings/wasm/typedoc.json b/bindings/wasm/typedoc.json index c6874ebe33..586d71d40d 100644 --- a/bindings/wasm/typedoc.json +++ b/bindings/wasm/typedoc.json @@ -1,23 +1,24 @@ { - "$schema": "https://typedoc.org/schema.json", - "disableSources": true, - "excludePrivate": true, - "excludeInternal": true, - "excludeNotDocumented": true, - "excludeExternals": true, - "entryPoints": ["./node/"], - "entryPointStrategy": "expand", - "tsconfig": "./tsconfig.typedoc.json", - "out": "./docs/wasm", - "plugin": ["typedoc-plugin-markdown"], - "readme": "none", - "githubPages": false, - "theme": "markdown", - "entryDocument": "api_ref.md", - "hideBreadcrumbs": true, - "hideGenerator": true, - "sort": ["source-order"], - "compilerOptions": { - "skipLibCheck": true, - } + "$schema": "https://typedoc.org/schema.json", + "disableSources": true, + "excludePrivate": true, + "excludeInternal": true, + "excludeNotDocumented": true, + "excludeExternals": true, + "entryPointStrategy": "expand", + "plugin": [ + "typedoc-plugin-markdown" + ], + "readme": "none", + "githubPages": false, + "theme": "markdown", + "entryFileName": "api_ref.md", + "hideBreadcrumbs": true, + "hideGenerator": true, + "sort": [ + "source-order" + ], + "compilerOptions": { + "skipLibCheck": true + } } diff --git a/dprint.json b/dprint.json index 97825bb4b5..9ce73b412d 100644 --- a/dprint.json +++ b/dprint.json @@ -12,7 +12,8 @@ "excludes": [ "documentation", "**/{node_modules,target}", - "bindings/wasm/{node,web}/**/*.{js,ts}" + "bindings/wasm/identity_wasm/{node,web}/**/*.{js,ts}", + "bindings/wasm/iota_interaction_ts/{node,web}/**/*.{js,ts}" ], "plugins": [ "https://plugins.dprint.dev/toml-0.5.1.wasm", diff --git a/examples/Cargo.toml b/examples/Cargo.toml index a280ec5d09..6c160e35be 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -26,6 +26,7 @@ features = [ "domain-linkage", "jpt-bbs-plus", "iota-client", + "send-sync-storage", "memstore", "resolver", "revocation-bitmap", diff --git a/identity_document/benches/deserialize_document.rs b/identity_document/benches/deserialize_document.rs index 1bd26ce870..0b5203b83c 100644 --- a/identity_document/benches/deserialize_document.rs +++ b/identity_document/benches/deserialize_document.rs @@ -220,9 +220,9 @@ fn deserialize_json_document(c: &mut Criterion) { (JSON_DOC_DID_KEY, "did:key document"), (JSON_DOCUMENT_LARGE, "large document"), ] { - group.throughput(Throughput::Bytes(json.as_bytes().len() as u64)); + group.throughput(Throughput::Bytes(json.len() as u64)); group.bench_with_input( - BenchmarkId::from_parameter(format!("{name}, document size: {} bytes", json.as_bytes().len())), + BenchmarkId::from_parameter(format!("{name}, document size: {} bytes", json.len())), json, |b, json| { b.iter(|| { diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 260004f95e..a0ecbcccf4 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -17,10 +17,16 @@ identity_credential = { version = "=1.4.0", path = "../identity_credential", fea identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } identity_iota_core = { version = "=1.4.0", path = "../identity_iota_core", default-features = false } -identity_resolver = { version = "=1.4.0", path = "../identity_resolver", default-features = false, optional = true } identity_storage = { version = "=1.4.0", path = "../identity_storage", default-features = false, features = ["iota-document"] } identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +identity_iota_interaction = { version = "=1.4.0", path = "../identity_iota_interaction" } +identity_resolver = { version = "=1.4.0", path = "../identity_resolver", default-features = false, optional = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +identity_iota_interaction = { version = "=1.4.0", path = "../identity_iota_interaction", default-features = false } + [dev-dependencies] # required for doc test anyhow = "1.0.64" @@ -30,13 +36,12 @@ rand = "0.8.5" tokio = { version = "1.29.0", features = ["full"] } [features] -default = ["revocation-bitmap", "iota-client", "resolver"] +default = ["revocation-bitmap", "iota-client", "send-sync", "resolver"] # Enables the IOTA client integration, and the `DidResolutionHandler` trait. iota-client = [ "identity_iota_core/iota-client", - "identity_resolver/iota", - "identity_storage/send-sync-storage", + "identity_resolver?/iota", "identity_storage/storage-signer", ] @@ -53,8 +58,12 @@ status-list-2021 = ["revocation-bitmap", "identity_credential/status-list-2021"] # Enables support for the `Resolver`. resolver = ["dep:identity_resolver"] +# Enables `Send` + `Sync` bounds for the storage and client interaction traits. +send-sync = ["send-sync-storage", "send-sync-client"] # Enables `Send` + `Sync` bounds for the storage traits. send-sync-storage = ["identity_storage/send-sync-storage"] +# Enables `Send` + `Sync` bounds for IOTA client interaction traits. +send-sync-client = ["identity_iota_core/send-sync-client-ext"] # Enables domain linkage support. domain-linkage = ["identity_credential/domain-linkage"] diff --git a/identity_iota/README.md b/identity_iota/README.md index 8690190a39..8aabe5364d 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -33,7 +33,7 @@ IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentra [Foreign Function Interface (FFI)](https://en.wikipedia.org/wiki/Foreign_function_interface) Bindings of this [Rust](https://www.rust-lang.org/) library to other programming languages: -- [Web Assembly](https://github.com/iotaledger/identity.rs/blob/feat/identity-rebased-alpha/bindings/wasm/) (JavaScript/TypeScript) --> +- [Web Assembly](https://github.com/iotaledger/identity.rs/blob/feat/identity-rebased-alpha/bindings/wasm/identity_wasm/) (JavaScript/TypeScript) --> ## gRPC diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 12a5d9dc4e..05dad2bc0c 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -17,6 +17,8 @@ clippy::missing_errors_doc )] +pub use identity_iota_interaction as iota_interaction; + pub mod core { //! Core Traits and Types @@ -75,16 +77,16 @@ pub mod prelude { pub use identity_iota_core::IotaDID; pub use identity_iota_core::IotaDocument; - #[cfg(feature = "resolver")] + #[cfg(all(feature = "resolver", not(target_arch = "wasm32")))] #[cfg_attr(docsrs, doc(cfg(feature = "resolver")))] pub use identity_iota_core::DidResolutionHandler; - #[cfg(feature = "resolver")] + #[cfg(all(feature = "resolver", not(target_arch = "wasm32")))] #[cfg_attr(docsrs, doc(cfg(feature = "resolver")))] pub use identity_resolver::Resolver; } -#[cfg(feature = "resolver")] +#[cfg(all(feature = "resolver", not(target_arch = "wasm32")))] #[cfg_attr(docsrs, doc(cfg(feature = "resolver")))] pub mod resolver { //! DID resolution utilities diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index e934c23b0b..87f9cf9f29 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -4,7 +4,7 @@ version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true -keywords = ["iota", "tangle", "utxo", "shimmer", "identity"] +keywords = ["iota", "tangle", "utxo", "identity"] license.workspace = true readme = "./README.md" repository.workspace = true @@ -14,6 +14,7 @@ description = "An IOTA Ledger integration for the IOTA DID Method." [dependencies] anyhow = "1.0.75" async-trait = { version = "0.1.81", default-features = false, optional = true } +cfg-if = "1.0.0" futures = { version = "0.3", default-features = false } identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } identity_credential = { version = "=1.4.0", path = "../identity_credential", default-features = false, features = ["validator"] } @@ -35,18 +36,28 @@ bcs = { version = "0.1.4", optional = true } fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto", optional = true } identity_eddsa_verifier = { version = "=1.4.0", path = "../identity_eddsa_verifier", optional = true } identity_jose = { version = "=1.4.0", path = "../identity_jose", optional = true } -iota-config = { git = "https://github.com/iotaledger/iota.git", package = "iota-config", tag = "v0.7.3-rc", optional = true } iota-crypto = { version = "0.23", optional = true } -iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.7.3-rc", optional = true } itertools = { version = "0.13.0", optional = true } -move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.7.3-rc", optional = true } phf = { version = "0.11.2", features = ["macros"] } rand = { version = "0.8.5", optional = true } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0", optional = true } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0", default-features = false, optional = true } serde-aux = { version = "4.5.0", optional = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +identity_iota_interaction = { version = "=1.4.0", path = "../identity_iota_interaction", optional = true } +iota-config = { git = "https://github.com/iotaledger/iota.git", package = "iota-config", tag = "v0.7.3-rc", optional = true } +iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.7.3-rc", optional = true } +move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.7.3-rc", optional = true } shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.7.3-rc", optional = true } tokio = { version = "1.29.0", default-features = false, optional = true, features = ["macros", "sync", "rt", "process"] } +[target.'cfg(target_arch = "wasm32")'.dependencies] +identity_iota_interaction = { version = "=1.4.0", path = "../identity_iota_interaction", default-features = false, optional = true } +# Dependency iota_interaction_ts is always used on wasm32 platform. It is not controlled by the "iota-client" feature +# because ut's unclear how to implement this. wasm32 build will most probably always use the "iota-client" feature +# so this seems to be tolerable for now. +iota_interaction_ts = { version = "=1.4.0", path = "../bindings/wasm/iota_interaction_ts" } + [dev-dependencies] iota-crypto = { version = "0.23", default-features = false, features = ["bip39", "bip39-en"] } proptest = { version = "1.0.0", default-features = false, features = ["std"] } @@ -65,13 +76,14 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["iota-client", "revocation-bitmap", "send-sync-client-ext"] +default = ["iota-client", "revocation-bitmap", "send-sync"] # Enables the IOTA Client related components, and dependencies. iota-client = [ "dep:async-trait", "dep:bcs", "dep:fastcrypto", "dep:identity_eddsa_verifier", + "dep:identity_iota_interaction", "dep:identity_jose", "dep:iota-config", "dep:iota-crypto", @@ -84,9 +96,15 @@ iota-client = [ "dep:shared-crypto", "dep:tokio", ] + # Enables revocation with `RevocationBitmap2022`. revocation-bitmap = ["identity_credential/revocation-bitmap"] -# Adds Send bounds on the futures produces by the client extension traits. + +# Enables `Send` + `Sync` bounds for the storage and client interaction traits. +send-sync = ["send-sync-storage", "send-sync-client-ext"] +# Enables `Send` + `Sync` bounds for the storage traits. +send-sync-storage = ["secret-storage?/send-sync-storage"] +# Enables `Send` + `Sync` bounds for IOTA client interaction traits. send-sync-client-ext = [] [lints] diff --git a/identity_iota_core/README.md b/identity_iota_core/README.md index 0d97aa5848..2a3862d52b 100644 --- a/identity_iota_core/README.md +++ b/identity_iota_core/README.md @@ -11,10 +11,10 @@ You can run the tests as usual with: cargo test ``` -The e2e should be run against against a [local network](https://docs.iota.org/developer/getting-started/local-network), as this makes funding way more easy, as the local faucet can be used deliberately. +The e2e should be run against a [local network](https://docs.iota.org/developer/getting-started/local-network), as this makes funding way more easy, as the local faucet can be used deliberately. ### Running the tests with active-address-funding -When you're not running the tests locally, you might notice some restrictions in regards of interactions with the faucet. The current e2e test setup creates new test accounts for every test to avoid test pollution, but those accounts requests funds from a faucet. That faucet might have restrictions on how much funds an IP can request in a certain time range. For example, this might happen when trying to run the tests against `devnet`. +When you're not running the tests locally, you might notice some restrictions in regards of interactions with the faucet. The current e2e test setup creates new test accounts for every test to avoid test pollution, but those accounts request funds from a faucet. That faucet might have restrictions on how much funds an IP can request in a certain time range. For example, this might happen when trying to run the tests against `devnet`. As we want to verify that our API works as expected on this environment as well, a toggle has been added to change the behavior in the tests to not request the faucet for funds, but use the active account of the IOTA CLI to send funds to new test users. This is not the default test behavior and should only be used in edge cases, as it comes with a few caveats, that might not be desired to have in the tests: diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index c40b34104b..db1989772b 100644 --- a/identity_iota_core/src/document/iota_document.rs +++ b/identity_iota_core/src/document/iota_document.rs @@ -398,7 +398,7 @@ impl IotaDocument { mod client_document { use identity_core::common::Timestamp; use identity_did::DID; - use iota_sdk::rpc_types::IotaObjectData; + use identity_iota_interaction::rpc_types::IotaObjectData; use crate::rebased::migration::unpack_identity_data; diff --git a/identity_iota_core/src/iota_interaction_adapter.rs b/identity_iota_core/src/iota_interaction_adapter.rs new file mode 100644 index 0000000000..c0b97a4e38 --- /dev/null +++ b/identity_iota_core/src/iota_interaction_adapter.rs @@ -0,0 +1,15 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// The following platform compile switch provides all the +// ...Adapter types from iota_interaction_rust or iota_interaction_ts +// like IotaClientAdapter, AssetMoveCallsAdapter, IdentityMoveCallsAdapter, +// TransactionBuilderAdapter, MigrationMoveCallsAdapter, ... and so on + +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + pub(crate) use iota_interaction_ts::*; + } else { + pub(crate) use crate::iota_interaction_rust::*; + } +} diff --git a/identity_iota_core/src/iota_interaction_rust/asset_move_calls.rs b/identity_iota_core/src/iota_interaction_rust/asset_move_calls.rs new file mode 100644 index 0000000000..bf134d16a0 --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/asset_move_calls.rs @@ -0,0 +1,203 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; + +use crate::rebased::Error; +use identity_iota_interaction::ident_str; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::base_types::SequenceNumber; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::Command; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::AssetMoveCalls; +use identity_iota_interaction::MoveType; +use identity_iota_interaction::ProgrammableTransactionBcs; +use identity_iota_interaction::TypedValue; +use iota_sdk::types::transaction::Argument; +use iota_sdk::types::transaction::ProgrammableMoveCall; + +fn try_to_argument( + content: &T, + ptb: &mut ProgrammableTransactionBuilder, + package: ObjectID, +) -> Result { + match content.get_typed_value(package) { + TypedValue::IotaVerifiableCredential(value) => { + let values = ptb + .pure(value.data()) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + Ok(ptb.command(Command::MoveCall(Box::new(ProgrammableMoveCall { + package, + module: ident_str!("public_vc").into(), + function: ident_str!("new").into(), + type_arguments: vec![], + arguments: vec![values], + })))) + } + TypedValue::Other(value) => ptb.pure(value).map_err(|e| Error::InvalidArgument(e.to_string())), + } +} + +pub(crate) struct AssetMoveCallsRustSdk {} + +impl AssetMoveCalls for AssetMoveCallsRustSdk { + type Error = Error; + + fn new_asset( + inner: T, + mutable: bool, + transferable: bool, + deletable: bool, + package: ObjectID, + ) -> Result { + let mut ptb = ProgrammableTransactionBuilder::new(); + let inner = try_to_argument(&inner, &mut ptb, package)?; + let mutable = ptb.pure(mutable).map_err(|e| Error::InvalidArgument(e.to_string()))?; + let transferable = ptb + .pure(transferable) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + let deletable = ptb.pure(deletable).map_err(|e| Error::InvalidArgument(e.to_string()))?; + + ptb.command(Command::MoveCall(Box::new(ProgrammableMoveCall { + package, + module: ident_str!("asset").into(), + function: ident_str!("new_with_config").into(), + type_arguments: vec![T::move_type(package)], + arguments: vec![inner, mutable, transferable, deletable], + }))); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn delete(asset: ObjectRef, package: ObjectID) -> Result + where + T: MoveType, + { + let mut ptb = ProgrammableTransactionBuilder::new(); + + let asset = ptb + .obj(ObjectArg::ImmOrOwnedObject(asset)) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + + ptb.command(Command::move_call( + package, + ident_str!("asset").into(), + ident_str!("delete").into(), + vec![T::move_type(package)], + vec![asset], + )); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn transfer( + asset: ObjectRef, + recipient: IotaAddress, + package: ObjectID, + ) -> Result { + let mut ptb = ProgrammableTransactionBuilder::new(); + let asset = ptb + .obj(ObjectArg::ImmOrOwnedObject(asset)) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + let recipient = ptb.pure(recipient).map_err(|e| Error::InvalidArgument(e.to_string()))?; + + ptb.command(Command::move_call( + package, + ident_str!("asset").into(), + ident_str!("transfer").into(), + vec![T::move_type(package)], + vec![asset, recipient], + )); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn make_tx( + proposal: (ObjectID, SequenceNumber), + cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + function_name: &'static str, + ) -> Result { + let mut ptb = ProgrammableTransactionBuilder::new(); + let proposal = ptb + .obj(ObjectArg::SharedObject { + id: proposal.0, + initial_shared_version: proposal.1, + mutable: true, + }) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + let cap = ptb + .obj(ObjectArg::ImmOrOwnedObject(cap)) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + let asset = ptb + .obj(ObjectArg::Receiving(asset)) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + + ptb.command(Command::move_call( + package, + ident_str!("asset").into(), + ident_str!(function_name).into(), + vec![asset_type_param], + vec![proposal, cap, asset], + )); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn accept_proposal( + proposal: (ObjectID, SequenceNumber), + recipient_cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + ) -> Result { + Self::make_tx(proposal, recipient_cap, asset, asset_type_param, package, "accept") + } + + fn conclude_or_cancel( + proposal: (ObjectID, SequenceNumber), + sender_cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + ) -> Result { + Self::make_tx( + proposal, + sender_cap, + asset, + asset_type_param, + package, + "conclude_or_cancel", + ) + } + + fn update(asset: ObjectRef, new_content: T, package: ObjectID) -> Result + where + T: MoveType + Serialize, + { + let mut ptb = ProgrammableTransactionBuilder::new(); + + let asset = ptb + .obj(ObjectArg::ImmOrOwnedObject(asset)) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + let new_content = ptb + .pure(new_content) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + + ptb.command(Command::move_call( + package, + ident_str!("asset").into(), + ident_str!("set_content").into(), + vec![T::move_type(package)], + vec![asset, new_content], + )); + + Ok(bcs::to_bytes(&ptb.finish())?) + } +} diff --git a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs new file mode 100644 index 0000000000..2f94a53596 --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs @@ -0,0 +1,896 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use itertools::Itertools; + +use std::collections::HashSet; +use std::str::FromStr; + +use identity_iota_interaction::ident_str; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::base_types::ObjectType; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as PrgrTxBuilder; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use identity_iota_interaction::BorrowIntentFnInternalT; +use identity_iota_interaction::ControllerIntentFnInternalT; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::MoveType; +use identity_iota_interaction::ProgrammableTransactionBcs; +use identity_iota_interaction::TransactionBuilderT; + +use super::transaction_builder::TransactionBuilderRustSdk; +use super::utils; + +use crate::rebased::proposals::BorrowAction; +use crate::rebased::proposals::ControllerExecution; +use crate::rebased::proposals::SendAction; +use crate::rebased::rebased_err; +use crate::rebased::Error; + +struct ProposalContext { + ptb: PrgrTxBuilder, + controller_cap: Argument, + delegation_token: Argument, + borrow: Argument, + identity: Argument, + proposal_id: Argument, +} + +fn borrow_proposal_impl( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + expiration: Option, + package_id: ObjectID, +) -> anyhow::Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, cap_arg, package_id); + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; + let exp_arg = utils::option_to_move(expiration, &mut ptb, package_id)?; + let objects_arg = ptb.pure(objects)?; + + let proposal_id = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("propose_borrow").into(), + vec![], + vec![identity_arg, delegation_token, exp_arg, objects_arg], + ); + + Ok(ProposalContext { + ptb, + identity: identity_arg, + controller_cap: cap_arg, + delegation_token, + borrow, + proposal_id, + }) +} + +fn execute_borrow_impl>( + ptb: &mut PrgrTxBuilder, + identity: Argument, + delegation_token: Argument, + proposal_id: Argument, + objects: Vec, + intent_fn: F, + package: ObjectID, +) -> anyhow::Result<()> { + // Get the proposal's action as argument. + let borrow_action = ptb.programmable_move_call( + package, + move_core_types::ident_str!("identity").into(), + move_core_types::ident_str!("execute_proposal").into(), + vec![BorrowAction::move_type(package)], + vec![identity, delegation_token, proposal_id], + ); + + // Borrow all the objects specified in the action. + let obj_arg_map = objects + .into_iter() + .map(|obj_data| { + let obj_ref = obj_data.object_ref(); + let ObjectType::Struct(obj_type) = obj_data.object_type()? else { + unreachable!("move packages cannot be borrowed to begin with"); + }; + let recv_obj = ptb.obj(ObjectArg::Receiving(obj_ref))?; + + let obj_arg = ptb.programmable_move_call( + package, + move_core_types::ident_str!("identity").into(), + move_core_types::ident_str!("execute_borrow").into(), + vec![obj_type.into()], + vec![identity, borrow_action, recv_obj], + ); + + Ok((obj_ref.0, (obj_arg, obj_data))) + }) + .collect::>()?; + + // Apply the user-defined operation. + intent_fn(ptb, &obj_arg_map); + + // Put back all the objects. + obj_arg_map.into_values().for_each(|(obj_arg, obj_data)| { + let ObjectType::Struct(obj_type) = obj_data.object_type().expect("checked above") else { + unreachable!("move packages cannot be borrowed to begin with"); + }; + ptb.programmable_move_call( + package, + move_core_types::ident_str!("borrow_proposal").into(), + move_core_types::ident_str!("put_back").into(), + vec![obj_type.into()], + vec![borrow_action, obj_arg], + ); + }); + + // Consume the now empty borrow_action + ptb.programmable_move_call( + package, + move_core_types::ident_str!("borrow_proposal").into(), + move_core_types::ident_str!("conclude_borrow").into(), + vec![], + vec![borrow_action], + ); + + Ok(()) +} + +fn controller_execution_impl( + identity: OwnedObjectRef, + capability: ObjectRef, + controller_cap_id: ObjectID, + expiration: Option, + package_id: ObjectID, +) -> anyhow::Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, cap_arg, package_id); + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; + let controller_cap_id = ptb.pure(controller_cap_id)?; + let exp_arg = utils::option_to_move(expiration, &mut ptb, package_id)?; + + let proposal_id = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("propose_controller_execution").into(), + vec![], + vec![identity_arg, delegation_token, controller_cap_id, exp_arg], + ); + + Ok(ProposalContext { + ptb, + controller_cap: cap_arg, + delegation_token, + borrow, + identity: identity_arg, + proposal_id, + }) +} + +fn execute_controller_execution_impl>( + ptb: &mut PrgrTxBuilder, + identity: Argument, + proposal_id: Argument, + delegation_token: Argument, + borrowing_controller_cap_ref: ObjectRef, + intent_fn: F, + package: ObjectID, +) -> anyhow::Result<()> { + // Get the proposal's action as argument. + let controller_execution_action = ptb.programmable_move_call( + package, + ident_str!("identity").into(), + ident_str!("execute_proposal").into(), + vec![ControllerExecution::move_type(package)], + vec![identity, delegation_token, proposal_id], + ); + + // Borrow the controller cap into this transaction. + let receiving = ptb.obj(ObjectArg::Receiving(borrowing_controller_cap_ref))?; + let borrowed_controller_cap = ptb.programmable_move_call( + package, + ident_str!("identity").into(), + ident_str!("borrow_controller_cap").into(), + vec![], + vec![identity, controller_execution_action, receiving], + ); + + // Apply the user-defined operation. + intent_fn(ptb, &borrowed_controller_cap); + + // Put back the borrowed controller cap. + ptb.programmable_move_call( + package, + ident_str!("controller_proposal").into(), + ident_str!("put_back").into(), + vec![], + vec![controller_execution_action, borrowed_controller_cap], + ); + + Ok(()) +} + +fn send_proposal_impl( + identity: OwnedObjectRef, + capability: ObjectRef, + transfer_map: Vec<(ObjectID, IotaAddress)>, + expiration: Option, + package_id: ObjectID, +) -> anyhow::Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, cap_arg, package_id); + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; + let exp_arg = utils::option_to_move(expiration, &mut ptb, package_id)?; + let (objects, recipients) = { + let (objects, recipients): (Vec<_>, Vec<_>) = transfer_map.into_iter().unzip(); + let objects = ptb.pure(objects)?; + let recipients = ptb.pure(recipients)?; + + (objects, recipients) + }; + + let proposal_id = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("propose_send").into(), + vec![], + vec![identity_arg, delegation_token, exp_arg, objects, recipients], + ); + + Ok(ProposalContext { + ptb, + identity: identity_arg, + controller_cap: cap_arg, + delegation_token, + borrow, + proposal_id, + }) +} + +fn execute_send_impl( + ptb: &mut PrgrTxBuilder, + identity: Argument, + delegation_token: Argument, + proposal_id: Argument, + objects: Vec<(ObjectRef, TypeTag)>, + package: ObjectID, +) -> anyhow::Result<()> { + // Get the proposal's action as argument. + let send_action = ptb.programmable_move_call( + package, + ident_str!("identity").into(), + ident_str!("execute_proposal").into(), + vec![SendAction::move_type(package)], + vec![identity, delegation_token, proposal_id], + ); + + // Send each object in this send action. + // Traversing the map in reverse reduces the number of operations on the move side. + for (obj, obj_type) in objects.into_iter().rev() { + let recv_obj = ptb.obj(ObjectArg::Receiving(obj))?; + + ptb.programmable_move_call( + package, + ident_str!("identity").into(), + ident_str!("execute_send").into(), + vec![obj_type], + vec![identity, send_action, recv_obj], + ); + } + + // Consume the now empty send_action + ptb.programmable_move_call( + package, + ident_str!("transfer_proposal").into(), + ident_str!("complete_send").into(), + vec![], + vec![send_action], + ); + + Ok(()) +} + +#[derive(Clone)] +pub(crate) struct IdentityMoveCallsRustSdk {} + +impl IdentityMoveCalls for IdentityMoveCallsRustSdk { + type Error = Error; + type NativeTxBuilder = PrgrTxBuilder; + + fn propose_borrow( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let ProposalContext { + mut ptb, + controller_cap, + delegation_token, + borrow, + .. + } = borrow_proposal_impl(identity, capability, objects, expiration, package_id)?; + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn execute_borrow>( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + objects: Vec, + intent_fn: F, + package: ObjectID, + ) -> Result { + let mut internal_ptb = TransactionBuilderRustSdk::new(PrgrTxBuilder::new()); + let ptb = internal_ptb.as_native_tx_builder(); + let identity = utils::owned_ref_to_shared_object_arg(identity, ptb, true)?; + let controller_cap = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; + let (delegation_token, borrow) = utils::get_controller_delegation(ptb, controller_cap, package); + let proposal_id = ptb.pure(proposal_id)?; + + execute_borrow_impl( + ptb, + identity, + delegation_token, + proposal_id, + objects, + intent_fn, + package, + )?; + + utils::put_back_delegation_token(ptb, controller_cap, delegation_token, borrow, package); + + internal_ptb.finish() + } + + fn create_and_execute_borrow>( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + intent_fn: F, + expiration: Option, + package_id: ObjectID, + ) -> anyhow::Result { + let ProposalContext { + mut ptb, + controller_cap, + delegation_token, + borrow, + identity, + proposal_id, + } = borrow_proposal_impl( + identity, + capability, + objects.iter().map(|obj_data| obj_data.object_id).collect_vec(), + expiration, + package_id, + )?; + + execute_borrow_impl( + &mut ptb, + identity, + delegation_token, + proposal_id, + objects, + intent_fn, + package_id, + )?; + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn propose_config_change( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + expiration: Option, + threshold: Option, + controllers_to_add: I1, + controllers_to_remove: HashSet, + controllers_to_update: I2, + package: ObjectID, + ) -> Result + where + I1: IntoIterator, + I2: IntoIterator, + { + let mut ptb = PrgrTxBuilder::new(); + + let controllers_to_add = { + let (addresses, vps): (Vec, Vec) = controllers_to_add.into_iter().unzip(); + let addresses = ptb.pure(addresses).map_err(rebased_err)?; + let vps = ptb.pure(vps).map_err(rebased_err)?; + + ptb.programmable_move_call( + package, + ident_str!("utils").into(), + ident_str!("vec_map_from_keys_values").into(), + vec![TypeTag::Address, TypeTag::U64], + vec![addresses, vps], + ) + }; + let controllers_to_update = { + let (ids, vps): (Vec, Vec) = controllers_to_update.into_iter().unzip(); + let ids = ptb.pure(ids).map_err(rebased_err)?; + let vps = ptb.pure(vps).map_err(rebased_err)?; + + ptb.programmable_move_call( + package, + ident_str!("utils").into(), + ident_str!("vec_map_from_keys_values").into(), + vec![TypeTag::from_str("0x2::object::ID").expect("valid utf8"), TypeTag::U64], + vec![ids, vps], + ) + }; + let identity = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true).map_err(rebased_err)?; + let controller_cap = ptb + .obj(ObjectArg::ImmOrOwnedObject(controller_cap)) + .map_err(rebased_err)?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, controller_cap, package); + let expiration = utils::option_to_move(expiration, &mut ptb, package).map_err(rebased_err)?; + let threshold = utils::option_to_move(threshold, &mut ptb, package).map_err(rebased_err)?; + let controllers_to_remove = ptb.pure(controllers_to_remove).map_err(rebased_err)?; + + let _proposal_id = ptb.programmable_move_call( + package, + ident_str!("identity").into(), + ident_str!("propose_config_change").into(), + vec![], + vec![ + identity, + delegation_token, + expiration, + threshold, + controllers_to_add, + controllers_to_remove, + controllers_to_update, + ], + ); + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn execute_config_change( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + proposal_id: ObjectID, + package: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + + let identity = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true).map_err(rebased_err)?; + let controller_cap = ptb + .obj(ObjectArg::ImmOrOwnedObject(controller_cap)) + .map_err(rebased_err)?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, controller_cap, package); + let proposal_id = ptb.pure(proposal_id).map_err(rebased_err)?; + ptb.programmable_move_call( + package, + ident_str!("identity").into(), + ident_str!("execute_config_change").into(), + vec![], + vec![identity, delegation_token, proposal_id], + ); + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn propose_controller_execution( + identity: OwnedObjectRef, + capability: ObjectRef, + controller_cap_id: ObjectID, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let ProposalContext { + mut ptb, + controller_cap, + delegation_token, + borrow, + .. + } = controller_execution_impl(identity, capability, controller_cap_id, expiration, package_id)?; + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn execute_controller_execution>( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + borrowing_controller_cap_ref: ObjectRef, + intent_fn: F, + package: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let identity = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; + let controller_cap = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, controller_cap, package); + let proposal_id = ptb.pure(proposal_id)?; + + execute_controller_execution_impl( + &mut ptb, + identity, + proposal_id, + delegation_token, + borrowing_controller_cap_ref, + intent_fn, + package, + )?; + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn create_and_execute_controller_execution>( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + borrowing_controller_cap_ref: ObjectRef, + intent_fn: F, + package_id: ObjectID, + ) -> Result { + let ProposalContext { + mut ptb, + controller_cap, + delegation_token, + borrow, + proposal_id, + identity, + } = controller_execution_impl( + identity, + capability, + borrowing_controller_cap_ref.0, + expiration, + package_id, + )?; + + execute_controller_execution_impl( + &mut ptb, + identity, + proposal_id, + delegation_token, + borrowing_controller_cap_ref, + intent_fn, + package_id, + )?; + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let doc_arg = utils::ptb_pure(&mut ptb, "did_doc", did_doc)?; + let clock = utils::get_clock_ref(&mut ptb); + + // Create a new identity, sending its capability to the tx's sender. + let _identity_id = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("new").into(), + vec![], + vec![doc_arg, clock], + ); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn new_with_controllers( + did_doc: &[u8], + controllers: C, + threshold: u64, + package_id: ObjectID, + ) -> Result + where + C: IntoIterator, + { + let mut ptb = PrgrTxBuilder::new(); + + let controllers = { + let (ids, vps): (Vec, Vec) = controllers.into_iter().unzip(); + let ids = ptb.pure(ids).map_err(|e| Error::InvalidArgument(e.to_string()))?; + let vps = ptb.pure(vps).map_err(|e| Error::InvalidArgument(e.to_string()))?; + ptb.programmable_move_call( + package_id, + ident_str!("utils").into(), + ident_str!("vec_map_from_keys_values").into(), + vec![TypeTag::Address, TypeTag::U64], + vec![ids, vps], + ) + }; + + let controllers_that_can_delegate = ptb.programmable_move_call( + IOTA_FRAMEWORK_PACKAGE_ID, + ident_str!("vec_map").into(), + ident_str!("empty").into(), + vec![TypeTag::Address, TypeTag::U64], + vec![], + ); + let doc_arg = ptb.pure(did_doc).map_err(|e| Error::InvalidArgument(e.to_string()))?; + let threshold_arg = ptb.pure(threshold).map_err(|e| Error::InvalidArgument(e.to_string()))?; + let clock = utils::get_clock_ref(&mut ptb); + + // Create a new identity, sending its capabilities to the specified controllers. + let _identity_id = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("new_with_controllers").into(), + vec![], + vec![ + doc_arg, + controllers, + controllers_that_can_delegate, + threshold_arg, + clock, + ], + ); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn propose_deactivation( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability)).map_err(rebased_err)?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, cap_arg, package_id); + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true).map_err(rebased_err)?; + let exp_arg = utils::option_to_move(expiration, &mut ptb, package_id).map_err(rebased_err)?; + let clock = utils::get_clock_ref(&mut ptb); + + let _proposal_id = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("propose_deactivation").into(), + vec![], + vec![identity_arg, delegation_token, exp_arg, clock], + ); + + utils::put_back_delegation_token(&mut ptb, cap_arg, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn execute_deactivation( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability)).map_err(rebased_err)?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, cap_arg, package_id); + let proposal_id = ptb.pure(proposal_id).map_err(rebased_err)?; + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true).map_err(rebased_err)?; + let clock = utils::get_clock_ref(&mut ptb); + + let _ = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("execute_deactivation").into(), + vec![], + vec![identity_arg, delegation_token, proposal_id, clock], + ); + + utils::put_back_delegation_token(&mut ptb, cap_arg, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn approve_proposal( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + proposal_id: ObjectID, + package: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let identity = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true) + .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; + let controller_cap = ptb + .obj(ObjectArg::ImmOrOwnedObject(controller_cap)) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, controller_cap, package); + let proposal_id = ptb + .pure(proposal_id) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + + ptb.programmable_move_call( + package, + ident_str!("identity").into(), + ident_str!("approve_proposal").into(), + vec![T::move_type(package)], + vec![identity, delegation_token, proposal_id], + ); + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn propose_send( + identity: OwnedObjectRef, + capability: ObjectRef, + transfer_map: Vec<(ObjectID, IotaAddress)>, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let ProposalContext { + mut ptb, + controller_cap, + delegation_token, + borrow, + .. + } = send_proposal_impl(identity, capability, transfer_map, expiration, package_id)?; + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn execute_send( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + objects: Vec<(ObjectRef, TypeTag)>, + package: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let identity = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true)?; + let controller_cap = ptb.obj(ObjectArg::ImmOrOwnedObject(capability))?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, controller_cap, package); + let proposal_id = ptb.pure(proposal_id)?; + + execute_send_impl(&mut ptb, identity, delegation_token, proposal_id, objects, package)?; + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn create_and_execute_send( + identity: OwnedObjectRef, + capability: ObjectRef, + transfer_map: Vec<(ObjectID, IotaAddress)>, + expiration: Option, + objects: Vec<(ObjectRef, TypeTag)>, + package: ObjectID, + ) -> anyhow::Result { + let ProposalContext { + mut ptb, + identity, + controller_cap, + delegation_token, + borrow, + proposal_id, + } = send_proposal_impl(identity, capability, transfer_map, expiration, package)?; + + execute_send_impl(&mut ptb, identity, delegation_token, proposal_id, objects, package)?; + + utils::put_back_delegation_token(&mut ptb, controller_cap, delegation_token, borrow, package); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn propose_update( + identity: OwnedObjectRef, + capability: ObjectRef, + did_doc: impl AsRef<[u8]>, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability)).map_err(rebased_err)?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, cap_arg, package_id); + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true).map_err(rebased_err)?; + let exp_arg = utils::option_to_move(expiration, &mut ptb, package_id).map_err(rebased_err)?; + let doc_arg = ptb.pure(did_doc.as_ref()).map_err(rebased_err)?; + let clock = utils::get_clock_ref(&mut ptb); + + let _proposal_id = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("propose_update").into(), + vec![], + vec![identity_arg, delegation_token, doc_arg, exp_arg, clock], + ); + + utils::put_back_delegation_token(&mut ptb, cap_arg, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn execute_update( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability)).map_err(rebased_err)?; + let (delegation_token, borrow) = utils::get_controller_delegation(&mut ptb, cap_arg, package_id); + let proposal_id = ptb.pure(proposal_id).map_err(rebased_err)?; + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true).map_err(rebased_err)?; + let clock = utils::get_clock_ref(&mut ptb); + + let _ = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("execute_update").into(), + vec![], + vec![identity_arg, delegation_token, proposal_id, clock], + ); + + utils::put_back_delegation_token(&mut ptb, cap_arg, delegation_token, borrow, package_id); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn propose_upgrade( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability)).map_err(rebased_err)?; + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true).map_err(rebased_err)?; + let exp_arg = utils::option_to_move(expiration, &mut ptb, package_id).map_err(rebased_err)?; + + let _proposal_id = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("propose_upgrade").into(), + vec![], + vec![identity_arg, cap_arg, exp_arg], + ); + + Ok(bcs::to_bytes(&ptb.finish())?) + } + + fn execute_upgrade( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result { + let mut ptb = PrgrTxBuilder::new(); + let cap_arg = ptb.obj(ObjectArg::ImmOrOwnedObject(capability)).map_err(rebased_err)?; + let proposal_id = ptb.pure(proposal_id).map_err(rebased_err)?; + let identity_arg = utils::owned_ref_to_shared_object_arg(identity, &mut ptb, true).map_err(rebased_err)?; + + let _ = ptb.programmable_move_call( + package_id, + ident_str!("identity").into(), + ident_str!("execute_upgrade").into(), + vec![], + vec![identity_arg, cap_arg, proposal_id], + ); + + Ok(bcs::to_bytes(&ptb.finish())?) + } +} diff --git a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs new file mode 100644 index 0000000000..95c766eaac --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -0,0 +1,615 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use std::boxed::Box; +use std::marker::Send; +use std::option::Option; +use std::result::Result; + +use fastcrypto::hash::Blake2b256; +use fastcrypto::traits::ToFromBytes; +use secret_storage::Signer; + +use crate::rebased::Error; +use identity_iota_interaction::apis::CoinReadApi; +use identity_iota_interaction::apis::EventApi; +use identity_iota_interaction::apis::QuorumDriverApi; +use identity_iota_interaction::apis::ReadApi; +use identity_iota_interaction::error::IotaRpcResult; +use identity_iota_interaction::rpc_types::Coin; +use identity_iota_interaction::rpc_types::CoinPage; +use identity_iota_interaction::rpc_types::EventFilter; +use identity_iota_interaction::rpc_types::EventPage; +use identity_iota_interaction::rpc_types::IotaExecutionStatus; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::IotaObjectDataOptions; +use identity_iota_interaction::rpc_types::IotaObjectResponse; +use identity_iota_interaction::rpc_types::IotaObjectResponseQuery; +use identity_iota_interaction::rpc_types::IotaPastObjectResponse; +use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; +use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI; +use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsV1; +use identity_iota_interaction::rpc_types::IotaTransactionBlockResponse; +use identity_iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; +use identity_iota_interaction::rpc_types::ObjectChange; +use identity_iota_interaction::rpc_types::ObjectsPage; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::shared_crypto::intent::Intent; +use identity_iota_interaction::shared_crypto::intent::IntentMessage; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::SequenceNumber; +use identity_iota_interaction::types::crypto::Signature; +use identity_iota_interaction::types::crypto::SignatureScheme; +use identity_iota_interaction::types::digests::TransactionDigest; +use identity_iota_interaction::types::dynamic_field::DynamicFieldName; +use identity_iota_interaction::types::event::EventID; +use identity_iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::types::transaction::Transaction; +use identity_iota_interaction::types::transaction::TransactionData; +use identity_iota_interaction::CoinReadTrait; +use identity_iota_interaction::EventTrait; +use identity_iota_interaction::IotaClient; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::ProgrammableTransactionBcs; +use identity_iota_interaction::QuorumDriverTrait; +use identity_iota_interaction::ReadTrait; +use identity_iota_interaction::SignatureBcs; +use identity_iota_interaction::TransactionDataBcs; + +/// The minimum balance required to execute a transaction. +pub(crate) const MINIMUM_BALANCE: u64 = 1_000_000_000; + +#[allow(unreachable_pub, dead_code)] +pub trait IotaTransactionBlockResponseAdaptedT: + IotaTransactionBlockResponseT +{ +} +impl IotaTransactionBlockResponseAdaptedT for T where + T: IotaTransactionBlockResponseT +{ +} +#[allow(unreachable_pub, dead_code)] +pub type IotaTransactionBlockResponseAdaptedTraitObj = + Box>; + +#[allow(unreachable_pub, dead_code)] +pub trait QuorumDriverApiAdaptedT: + QuorumDriverTrait +{ +} +impl QuorumDriverApiAdaptedT for T where + T: QuorumDriverTrait +{ +} +#[allow(unreachable_pub, dead_code)] +pub type QuorumDriverApiAdaptedTraitObj = + Box>; + +#[allow(unreachable_pub, dead_code)] +pub trait ReadApiAdaptedT: ReadTrait {} +impl ReadApiAdaptedT for T where T: ReadTrait {} +#[allow(unreachable_pub, dead_code)] +pub type ReadApiAdaptedTraitObj = Box>; + +#[allow(unreachable_pub, dead_code)] +pub trait CoinReadApiAdaptedT: CoinReadTrait {} +impl CoinReadApiAdaptedT for T where T: CoinReadTrait {} +#[allow(unreachable_pub, dead_code)] +pub type CoinReadApiAdaptedTraitObj = Box>; + +#[allow(unreachable_pub, dead_code)] +pub trait EventApiAdaptedT: EventTrait {} +impl EventApiAdaptedT for T where T: EventTrait {} +#[allow(unreachable_pub, dead_code)] +pub type EventApiAdaptedTraitObj = Box>; + +#[allow(unreachable_pub, dead_code)] +pub trait IotaClientAdaptedT: IotaClientTrait {} +impl IotaClientAdaptedT for T where T: IotaClientTrait {} +#[allow(unreachable_pub, dead_code)] +pub type IotaClientAdaptedTraitObj = + Box>; + +pub struct IotaTransactionBlockResponseProvider { + response: IotaTransactionBlockResponse, +} + +impl IotaTransactionBlockResponseProvider { + pub(crate) fn new(response: IotaTransactionBlockResponse) -> Self { + IotaTransactionBlockResponseProvider { response } + } +} + +impl IotaTransactionBlockResponseT for IotaTransactionBlockResponseProvider { + type Error = Error; + type NativeResponse = IotaTransactionBlockResponse; + + fn effects_is_none(&self) -> bool { + self.response.effects.is_none() + } + + fn effects_is_some(&self) -> bool { + self.response.effects.is_some() + } + + fn to_string(&self) -> String { + format!("{:?}", self.response) + } + + fn effects_execution_status(&self) -> Option { + self.response.effects.as_ref().map(|effects| effects.status().clone()) + } + + fn effects_created(&self) -> Option> { + self.response.effects.as_ref().map(|effects| effects.created().to_vec()) + } + + fn as_native_response(&self) -> &Self::NativeResponse { + &self.response + } + + fn as_mut_native_response(&mut self) -> &mut Self::NativeResponse { + &mut self.response + } + + fn clone_native_response(&self) -> Self::NativeResponse { + self.response.clone() + } +} + +pub(crate) struct QuorumDriverAdapter<'a> { + api: &'a QuorumDriverApi, +} + +#[async_trait::async_trait()] +impl QuorumDriverTrait for QuorumDriverAdapter<'_> { + type Error = Error; + type NativeResponse = IotaTransactionBlockResponse; + + async fn execute_transaction_block( + &self, + tx_data_bcs: &TransactionDataBcs, + signatures: &[SignatureBcs], + options: Option, + request_type: Option, + ) -> IotaRpcResult { + let tx_data = bcs::from_bytes::(tx_data_bcs.as_slice())?; + let signatures_vec = signatures + .iter() + .map(|signature_bcs| bcs::from_bytes::(signature_bcs.as_slice())) + .collect::, _>>()?; + let tx = Transaction::from_data(tx_data, signatures_vec); + let response = self + .api + .execute_transaction_block(tx, options.unwrap_or_default(), request_type) + .await?; + Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) + } +} + +pub(crate) struct ReadAdapter<'a> { + api: &'a ReadApi, +} + +#[async_trait::async_trait()] +impl ReadTrait for ReadAdapter<'_> { + type Error = Error; + type NativeResponse = IotaTransactionBlockResponse; + + async fn get_chain_identifier(&self) -> Result { + self + .api + .get_chain_identifier() + .await + .map_err(|e| Error::Network("SDK get_chain_identifier() call failed".to_string(), e)) + } + + async fn get_dynamic_field_object( + &self, + parent_object_id: ObjectID, + name: DynamicFieldName, + ) -> IotaRpcResult { + self.api.get_dynamic_field_object(parent_object_id, name).await + } + + async fn get_object_with_options( + &self, + object_id: ObjectID, + options: IotaObjectDataOptions, + ) -> IotaRpcResult { + self.api.get_object_with_options(object_id, options).await + } + + async fn get_owned_objects( + &self, + address: IotaAddress, + query: Option, + cursor: Option, + limit: Option, + ) -> IotaRpcResult { + self.api.get_owned_objects(address, query, cursor, limit).await + } + + async fn get_reference_gas_price(&self) -> IotaRpcResult { + self.api.get_reference_gas_price().await + } + + async fn get_transaction_with_options( + &self, + digest: TransactionDigest, + options: IotaTransactionBlockResponseOptions, + ) -> IotaRpcResult { + let response = self.api.get_transaction_with_options(digest, options).await?; + Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) + } + + async fn try_get_parsed_past_object( + &self, + object_id: ObjectID, + version: SequenceNumber, + options: IotaObjectDataOptions, + ) -> IotaRpcResult { + self.api.try_get_parsed_past_object(object_id, version, options).await + } +} + +pub(crate) struct CoinReadAdapter<'a> { + api: &'a CoinReadApi, +} + +#[async_trait::async_trait()] +impl CoinReadTrait for CoinReadAdapter<'_> { + type Error = Error; + + async fn get_coins( + &self, + owner: IotaAddress, + coin_type: Option, + cursor: Option, + limit: Option, + ) -> IotaRpcResult { + self.api.get_coins(owner, coin_type, cursor, limit).await + } +} + +pub(crate) struct EventAdapter<'a> { + api: &'a EventApi, +} + +#[async_trait::async_trait()] +impl EventTrait for EventAdapter<'_> { + type Error = Error; + + async fn query_events( + &self, + query: EventFilter, + cursor: Option, + limit: Option, + descending_order: bool, + ) -> IotaRpcResult { + self.api.query_events(query, cursor, limit, descending_order).await + } +} + +#[derive(Clone)] +pub struct IotaClientRustSdk { + iota_client: IotaClient, +} + +#[async_trait] +impl IotaClientTrait for IotaClientRustSdk { + type Error = Error; + type NativeResponse = IotaTransactionBlockResponse; + + fn quorum_driver_api( + &self, + ) -> Box + Send + '_> { + Box::new(QuorumDriverAdapter { + api: self.iota_client.quorum_driver_api(), + }) + } + + fn read_api(&self) -> Box + Send + '_> { + Box::new(ReadAdapter { + api: self.iota_client.read_api(), + }) + } + + fn coin_read_api(&self) -> Box + Send + '_> { + Box::new(CoinReadAdapter { + api: self.iota_client.coin_read_api(), + }) + } + + fn event_api(&self) -> Box + Send + '_> { + Box::new(EventAdapter { + api: self.iota_client.event_api(), + }) + } + + async fn execute_transaction + Sync>( + &self, + sender_address: IotaAddress, + sender_public_key: &[u8], + tx_bcs: ProgrammableTransactionBcs, + gas_budget: Option, + signer: &S, + ) -> Result { + let tx = bcs::from_bytes::(tx_bcs.as_slice())?; + let response = self + .sdk_execute_transaction(sender_address, sender_public_key, tx, gas_budget, signer) + .await?; + Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) + } + + async fn default_gas_budget( + &self, + sender_address: IotaAddress, + tx_bcs: &ProgrammableTransactionBcs, + ) -> Result { + let tx = bcs::from_bytes::(tx_bcs.as_slice())?; + self.sdk_default_gas_budget(sender_address, &tx).await + } + + async fn get_previous_version(&self, iod: IotaObjectData) -> Result, Error> { + // try to get digest of previous tx + // if we requested the prev tx and it isn't returned, this should be the oldest state + let prev_tx_digest = if let Some(value) = iod.previous_transaction { + value + } else { + return Ok(None); + }; + + // resolve previous tx + let prev_tx_response = self + .iota_client + .read_api() + .get_transaction_with_options( + prev_tx_digest, + IotaTransactionBlockResponseOptions::new().with_object_changes(), + ) + .await + .map_err(|err| { + Error::InvalidIdentityHistory(format!("could not get previous transaction {prev_tx_digest}; {err}")) + })?; + + // check for updated/created changes + let (created, other_changes): (Vec, _) = prev_tx_response + .clone() + .object_changes + .ok_or_else(|| { + Error::InvalidIdentityHistory(format!( + "could not find object changes for object {} in transaction {prev_tx_digest}", + iod.object_id + )) + })? + .into_iter() + .filter(|elem| iod.object_id.eq(&elem.object_id())) + .partition(|elem| matches!(elem, ObjectChange::Created { .. })); + + // previous tx contain create tx, so there is no previous version + if created.len() == 1 { + return Ok(None); + } + + let mut previous_versions: Vec = other_changes + .iter() + .filter_map(|elem| match elem { + ObjectChange::Mutated { previous_version, .. } => Some(*previous_version), + _ => None, + }) + .collect(); + + previous_versions.sort(); + + let earliest_previous = if let Some(value) = previous_versions.first() { + value + } else { + return Ok(None); // no mutations in prev tx, so no more versions can be found + }; + + let past_obj_response = self.get_past_object(iod.object_id, *earliest_previous).await?; + match past_obj_response { + IotaPastObjectResponse::VersionFound(value) => Ok(Some(value)), + _ => Err(Error::InvalidIdentityHistory(format!( + "could not find previous version, past object response: {past_obj_response:?}" + ))), + } + } + + async fn get_past_object( + &self, + object_id: ObjectID, + version: SequenceNumber, + ) -> Result { + self + .iota_client + .read_api() + .try_get_parsed_past_object(object_id, version, IotaObjectDataOptions::full_content()) + .await + .map_err(|err| { + Error::InvalidIdentityHistory(format!("could not look up object {object_id} version {version}; {err}")) + }) + } +} + +impl IotaClientRustSdk { + pub fn new(iota_client: IotaClient) -> Result { + Ok(Self { iota_client }) + } + + async fn sdk_execute_transaction>( + &self, + sender_address: IotaAddress, + sender_public_key: &[u8], + tx: ProgrammableTransaction, + gas_budget: Option, + signer: &S, + ) -> Result { + let gas_budget = match gas_budget { + Some(gas) => gas, + None => self.sdk_default_gas_budget(sender_address, &tx).await?, + }; + let tx_data = self.get_transaction_data(tx, gas_budget, sender_address).await?; + let signature = Self::sign_transaction_data(signer, &tx_data, sender_public_key).await?; + + // execute tx + let response = self + .iota_client + .quorum_driver_api() + .execute_transaction_block( + Transaction::from_data(tx_data, vec![signature]), + IotaTransactionBlockResponseOptions::full_content(), + Some(ExecuteTransactionRequestType::WaitForLocalExecution), + ) + .await + .map_err(Error::TransactionExecutionFailed)?; + + if let Some(IotaTransactionBlockEffects::V1(IotaTransactionBlockEffectsV1 { + status: IotaExecutionStatus::Failure { error }, + .. + })) = &response.effects + { + Err(Error::TransactionUnexpectedResponse(error.to_string())) + } else { + Ok(response) + } + } + + async fn sdk_default_gas_budget( + &self, + sender_address: IotaAddress, + tx: &ProgrammableTransaction, + ) -> Result { + let gas_price = self + .iota_client + .read_api() + .get_reference_gas_price() + .await + .map_err(|e| Error::RpcError(e.to_string()))?; + let gas_coin = self.get_coin_for_transaction(sender_address).await?; + let tx_data = TransactionData::new_programmable( + sender_address, + vec![gas_coin.object_ref()], + tx.clone(), + 50_000_000, + gas_price, + ); + let dry_run_gas_result = self + .iota_client + .read_api() + .dry_run_transaction_block(tx_data) + .await? + .effects; + if dry_run_gas_result.status().is_err() { + let IotaExecutionStatus::Failure { error } = dry_run_gas_result.into_status() else { + unreachable!(); + }; + return Err(Error::TransactionUnexpectedResponse(error)); + } + let gas_summary = dry_run_gas_result.gas_cost_summary(); + let overhead = gas_price * 1000; + let net_used = gas_summary.net_gas_usage(); + let computation = gas_summary.computation_cost; + + let budget = overhead + (net_used.max(0) as u64).max(computation); + Ok(budget) + } + + async fn get_transaction_data( + &self, + programmable_transaction: ProgrammableTransaction, + gas_budget: u64, + sender_address: IotaAddress, + ) -> Result { + let gas_price = self + .iota_client + .read_api() + .get_reference_gas_price() + .await + .map_err(|err| Error::GasIssue(format!("could not get gas price; {err}")))?; + let coin = self.get_coin_for_transaction(sender_address).await?; + let tx_data = TransactionData::new_programmable( + sender_address, + vec![coin.object_ref()], + programmable_transaction, + gas_budget, + gas_price, + ); + + Ok(tx_data) + } + + async fn sign_transaction_data>( + signer: &S, + tx_data: &TransactionData, + sender_public_key: &[u8], + ) -> Result { + use fastcrypto::hash::HashFunction; + + let intent = Intent::iota_transaction(); + let intent_msg = IntentMessage::new(intent, tx_data); + let mut hasher = Blake2b256::default(); + let bcs_bytes = bcs::to_bytes(&intent_msg).map_err(|err| { + Error::TransactionSigningFailed(format!("could not serialize transaction message to bcs; {err}")) + })?; + hasher.update(bcs_bytes); + let digest = hasher.finalize().digest; + + let raw_signature = signer + .sign(&digest) + .await + .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}")))?; + + let binding = [ + [SignatureScheme::ED25519.flag()].as_slice(), + &raw_signature, + sender_public_key, + ] + .concat(); + let signature_bytes: &[u8] = binding.as_slice(); + + Signature::from_bytes(signature_bytes) + .map_err(|err| Error::TransactionSigningFailed(format!("could not parse signature to IOTA signature; {err}"))) + } + + async fn get_coin_for_transaction(&self, sender_address: IotaAddress) -> Result { + const LIMIT: usize = 10; + let mut cursor = None; + + loop { + let coins = self + .iota_client + .coin_read_api() + .get_coins(sender_address, None, cursor, Some(LIMIT)) + .await?; + + let Some(coin) = coins.data.into_iter().max_by_key(|coin| coin.balance) else { + return Err(Error::GasIssue(format!( + "no coins found for address {}", + sender_address + ))); + }; + + if coin.balance >= MINIMUM_BALANCE { + return Ok(coin); + } + + if !coins.has_next_page { + break; + } + + cursor = coins.next_cursor; + } + + Err(Error::GasIssue(format!( + "no coin found with minimum required balance of {} for address {}", + MINIMUM_BALANCE, sender_address + ))) + } +} diff --git a/identity_iota_core/src/iota_interaction_rust/migration_move_calls.rs b/identity_iota_core/src/iota_interaction_rust/migration_move_calls.rs new file mode 100644 index 0000000000..eaeb087cec --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/migration_move_calls.rs @@ -0,0 +1,56 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; + +use identity_iota_interaction::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use identity_iota_interaction::MigrationMoveCalls; +use identity_iota_interaction::ProgrammableTransactionBcs; + +use crate::rebased::Error; + +use super::utils; + +pub(crate) struct MigrationMoveCallsRustSdk {} + +impl MigrationMoveCalls for MigrationMoveCallsRustSdk { + type Error = Error; + + fn migrate_did_output( + did_output: ObjectRef, + creation_timestamp: Option, + migration_registry: OwnedObjectRef, + package: ObjectID, + ) -> anyhow::Result { + let mut ptb = Ptb::new(); + let did_output = ptb.obj(ObjectArg::ImmOrOwnedObject(did_output))?; + let migration_registry = utils::owned_ref_to_shared_object_arg(migration_registry, &mut ptb, true)?; + let clock = utils::get_clock_ref(&mut ptb); + + let creation_timestamp = match creation_timestamp { + Some(timestamp) => ptb.pure(timestamp)?, + _ => ptb.programmable_move_call( + IOTA_FRAMEWORK_PACKAGE_ID, + ident_str!("clock").into(), + ident_str!("timestamp_ms").into(), + vec![], + vec![clock], + ), + }; + + ptb.programmable_move_call( + package, + ident_str!("migration").into(), + ident_str!("migrate_alias_output").into(), + vec![], + vec![did_output, migration_registry, creation_timestamp, clock], + ); + + Ok(bcs::to_bytes(&ptb.finish())?) + } +} diff --git a/identity_iota_core/src/iota_interaction_rust/mod.rs b/identity_iota_core/src/iota_interaction_rust/mod.rs new file mode 100644 index 0000000000..9d085255b4 --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/mod.rs @@ -0,0 +1,47 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub(crate) mod asset_move_calls; +pub(crate) mod identity_move_calls; +pub(crate) mod iota_client_rust_sdk; +pub(crate) mod migration_move_calls; +pub(crate) mod transaction_builder; +mod utils; + +pub(crate) use asset_move_calls::AssetMoveCallsRustSdk as AssetMoveCallsAdapter; +pub(crate) use identity_move_calls::IdentityMoveCallsRustSdk as IdentityMoveCallsAdapter; +pub(crate) use iota_client_rust_sdk::IotaClientRustSdk as IotaClientAdapter; +pub(crate) use iota_client_rust_sdk::IotaTransactionBlockResponseProvider as IotaTransactionBlockResponseAdapter; +pub(crate) use migration_move_calls::MigrationMoveCallsRustSdk as MigrationMoveCallsAdapter; +#[allow(unused_imports)] +pub(crate) use transaction_builder::TransactionBuilderRustSdk as TransactionBuilderAdapter; + +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::CoinReadApiAdaptedT; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::CoinReadApiAdaptedTraitObj; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::EventApiAdaptedT; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::EventApiAdaptedTraitObj; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::IotaClientAdaptedT; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::IotaClientAdaptedTraitObj; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::IotaTransactionBlockResponseAdaptedT; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::IotaTransactionBlockResponseAdaptedTraitObj; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::QuorumDriverApiAdaptedT; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::QuorumDriverApiAdaptedTraitObj; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::ReadApiAdaptedT; +#[allow(unused_imports)] +pub(crate) use iota_client_rust_sdk::ReadApiAdaptedTraitObj; + +#[allow(unused_imports)] +pub(crate) use super::rebased::Error as AdapterError; +#[allow(unused_imports)] +pub(crate) use identity_iota_interaction::rpc_types::IotaTransactionBlockResponse as AdapterNativeResponse; diff --git a/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs b/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs new file mode 100644 index 0000000000..9854e1893a --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs @@ -0,0 +1,53 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::Deref; +use std::ops::DerefMut; + +use crate::rebased::Error; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::ProgrammableTransactionBcs; +use identity_iota_interaction::TransactionBuilderT; + +#[derive(Default)] +pub(crate) struct TransactionBuilderRustSdk { + pub(crate) builder: ProgrammableTransactionBuilder, +} + +impl TransactionBuilderRustSdk { + pub(crate) fn new(builder: ProgrammableTransactionBuilder) -> Self { + TransactionBuilderRustSdk { builder } + } +} + +impl TransactionBuilderT for TransactionBuilderRustSdk { + type Error = Error; + type NativeTxBuilder = ProgrammableTransactionBuilder; + + fn finish(self) -> Result { + let tx = self.builder.finish(); + Ok(bcs::to_bytes(&tx)?) + } + + fn as_native_tx_builder(&mut self) -> &mut Self::NativeTxBuilder { + &mut self.builder + } + + fn into_native_tx_builder(self) -> Self::NativeTxBuilder { + self.builder + } +} + +impl Deref for TransactionBuilderRustSdk { + type Target = ProgrammableTransactionBuilder; + + fn deref(&self) -> &Self::Target { + &self.builder + } +} + +impl DerefMut for TransactionBuilderRustSdk { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.builder + } +} diff --git a/identity_iota_core/src/iota_interaction_rust/utils.rs b/identity_iota_core/src/iota_interaction_rust/utils.rs new file mode 100644 index 0000000000..357d3647a3 --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/utils.rs @@ -0,0 +1,122 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::rebased::Error; +use identity_iota_interaction::move_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::STD_OPTION_MODULE_NAME; +use identity_iota_interaction::types::object::Owner; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::IOTA_CLOCK_OBJECT_ID; +use identity_iota_interaction::types::IOTA_CLOCK_OBJECT_SHARED_VERSION; +use identity_iota_interaction::types::MOVE_STDLIB_PACKAGE_ID; +use identity_iota_interaction::MoveType; +use serde::Serialize; + +/// Adds a reference to the on-chain clock to `ptb`'s arguments. +pub(crate) fn get_clock_ref(ptb: &mut Ptb) -> Argument { + ptb + .obj(ObjectArg::SharedObject { + id: IOTA_CLOCK_OBJECT_ID, + initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION, + mutable: false, + }) + .expect("network has a singleton clock instantiated") +} + +pub(crate) fn get_controller_delegation( + ptb: &mut Ptb, + controller_cap: Argument, + package: ObjectID, +) -> (Argument, Argument) { + let Argument::Result(idx) = ptb.programmable_move_call( + package, + ident_str!("controller").into(), + ident_str!("borrow").into(), + vec![], + vec![controller_cap], + ) else { + unreachable!("making move calls always return a result variant"); + }; + + (Argument::NestedResult(idx, 0), Argument::NestedResult(idx, 1)) +} + +pub(crate) fn put_back_delegation_token( + ptb: &mut Ptb, + controller_cap: Argument, + delegation_token: Argument, + borrow: Argument, + package: ObjectID, +) { + ptb.programmable_move_call( + package, + ident_str!("controller").into(), + ident_str!("put_back").into(), + vec![], + vec![controller_cap, delegation_token, borrow], + ); +} + +pub(crate) fn owned_ref_to_shared_object_arg( + owned_ref: OwnedObjectRef, + ptb: &mut Ptb, + mutable: bool, +) -> anyhow::Result { + let Owner::Shared { initial_shared_version } = owned_ref.owner else { + anyhow::bail!("Identity \"{}\" is not a shared object", owned_ref.object_id()); + }; + ptb.obj(ObjectArg::SharedObject { + id: owned_ref.object_id(), + initial_shared_version, + mutable, + }) +} + +pub(crate) fn option_to_move( + option: Option, + ptb: &mut Ptb, + package: ObjectID, +) -> Result { + let arg = if let Some(t) = option { + let t = ptb.pure(t)?; + ptb.programmable_move_call( + MOVE_STDLIB_PACKAGE_ID, + STD_OPTION_MODULE_NAME.into(), + ident_str!("some").into(), + vec![T::move_type(package)], + vec![t], + ) + } else { + ptb.programmable_move_call( + MOVE_STDLIB_PACKAGE_ID, + STD_OPTION_MODULE_NAME.into(), + ident_str!("none").into(), + vec![T::move_type(package)], + vec![], + ) + }; + + Ok(arg) +} + +pub(crate) fn ptb_pure(ptb: &mut Ptb, name: &str, value: T) -> Result +where + T: Serialize + core::fmt::Debug, +{ + ptb.pure(&value).map_err(|err| { + Error::InvalidArgument(format!( + r"could not serialize pure value {name} with value {value:?}; {err}" + )) + }) +} + +#[allow(dead_code)] +pub(crate) fn ptb_obj(ptb: &mut Ptb, name: &str, value: ObjectArg) -> Result { + ptb + .obj(value) + .map_err(|err| Error::InvalidArgument(format!("could not serialize object {name} {value:?}; {err}"))) +} diff --git a/identity_iota_core/src/lib.rs b/identity_iota_core/src/lib.rs index e076bbb7e4..012fe35d90 100644 --- a/identity_iota_core/src/lib.rs +++ b/identity_iota_core/src/lib.rs @@ -25,12 +25,18 @@ pub use self::error::Error; pub use self::error::Result; mod did; -#[cfg(feature = "iota-client")] -mod did_resolution; mod document; mod error; mod network; +mod state_metadata; + +#[cfg(feature = "iota-client")] +mod did_resolution; +#[cfg(feature = "iota-client")] +mod iota_interaction_adapter; +#[cfg(all(feature = "iota-client", not(target_arch = "wasm32")))] +/// IOTA Rust SDK based implementation of the identity_iota_interaction interface for non wasm targets. +mod iota_interaction_rust; #[cfg(feature = "iota-client")] /// Contains the rebased Identity and the interaction with the IOTA Client. pub mod rebased; -mod state_metadata; diff --git a/identity_iota_core/src/rebased/assets/asset.rs b/identity_iota_core/src/rebased/assets/asset.rs index a7e510bbef..83901fa4be 100644 --- a/identity_iota_core/src/rebased/assets/asset.rs +++ b/identity_iota_core/src/rebased/assets/asset.rs @@ -3,29 +3,30 @@ use std::str::FromStr as _; +use crate::iota_interaction_adapter::AssetMoveCallsAdapter; use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; -use crate::rebased::transaction::Transaction; -use crate::rebased::transaction::TransactionOutput; -use crate::rebased::utils::MoveType; +use crate::rebased::transaction::TransactionInternal; +use crate::rebased::transaction::TransactionOutputInternal; use crate::rebased::Error; use anyhow::anyhow; use anyhow::Context; use async_trait::async_trait; -use iota_sdk::rpc_types::IotaData as _; -use iota_sdk::rpc_types::IotaExecutionStatus; -use iota_sdk::rpc_types::IotaObjectDataOptions; -use iota_sdk::rpc_types::IotaTransactionBlockEffectsAPI as _; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::base_types::SequenceNumber; -use iota_sdk::types::id::UID; -use iota_sdk::types::object::Owner; -use iota_sdk::types::TypeTag; -use move_core_types::ident_str; -use move_core_types::language_storage::StructTag; +use identity_iota_interaction::ident_str; +use identity_iota_interaction::move_types::language_storage::StructTag; +use identity_iota_interaction::rpc_types::IotaData as _; +use identity_iota_interaction::rpc_types::IotaExecutionStatus; +use identity_iota_interaction::rpc_types::IotaObjectDataOptions; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::base_types::SequenceNumber; +use identity_iota_interaction::types::id::UID; +use identity_iota_interaction::types::object::Owner; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::AssetMoveCalls; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::MoveType; use secret_storage::Signer; use serde::de::DeserializeOwned; use serde::Deserialize; @@ -51,7 +52,7 @@ pub struct AuthenticatedAsset { fn deserialize_inner<'de, D, T>(deserializer: D) -> Result where D: Deserializer<'de>, - T: DeserializeOwned, + T: for<'a> Deserialize<'a>, { use serde::de::Error as _; @@ -65,7 +66,7 @@ where impl AuthenticatedAsset where - T: DeserializeOwned, + T: for<'a> Deserialize<'a>, { /// Resolves an [`AuthenticatedAsset`] by its ID `id`. pub async fn get_by_id(id: ObjectID, client: &IdentityClient) -> Result { @@ -374,32 +375,31 @@ pub struct UpdateContentTx<'a, T> { new_content: T, } -#[async_trait] -impl Transaction for UpdateContentTx<'_, T> +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for UpdateContentTx<'_, T> where T: MoveType + Serialize + Clone + Send + Sync, { type Output = (); - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { - let tx = move_calls::asset::update( + let tx = AssetMoveCallsAdapter::update( self.asset.object_ref(client).await?, self.new_content.clone(), client.package_id(), )?; let response = client.execute_transaction(tx, gas_budget).await?; let tx_status = response - .effects - .as_ref() + .effects_execution_status() .context("transaction had no effects") - .map(|effects| effects.status()) .map_err(|e| Error::TransactionUnexpectedResponse(e.to_string()))?; if let IotaExecutionStatus::Failure { error } = tx_status { @@ -408,7 +408,7 @@ where self.asset.inner = self.new_content; - Ok(TransactionOutput { output: (), response }) + Ok(TransactionOutputInternal { output: (), response }) } } @@ -416,44 +416,46 @@ where #[derive(Debug)] pub struct DeleteAssetTx(AuthenticatedAsset); -#[async_trait] -impl Transaction for DeleteAssetTx +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for DeleteAssetTx where T: MoveType + Send + Sync, { type Output = (); - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { let asset_ref = self.0.object_ref(client).await?; - let tx = move_calls::asset::delete::(asset_ref, client.package_id())?; + let tx = AssetMoveCallsAdapter::delete::(asset_ref, client.package_id())?; let response = client.execute_transaction(tx, gas_budget).await?; - Ok(TransactionOutput { output: (), response }) + Ok(TransactionOutputInternal { output: (), response }) } } /// A [`Transaction`] that creates a new [`AuthenticatedAsset`]. #[derive(Debug)] pub struct CreateAssetTx(AuthenticatedAssetBuilder); -#[async_trait] -impl Transaction for CreateAssetTx +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for CreateAssetTx where T: MoveType + Serialize + DeserializeOwned + Send, { type Output = AuthenticatedAsset; - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { @@ -463,22 +465,20 @@ where transferable, deletable, } = self.0; - let tx = move_calls::asset::new(inner, mutable, transferable, deletable, client.package_id())?; + let tx = AssetMoveCallsAdapter::new_asset(inner, mutable, transferable, deletable, client.package_id())?; let response = client.execute_transaction(tx, gas_budget).await?; let created_asset_id = response - .effects - .as_ref() + .effects_created() .ok_or_else(|| Error::TransactionUnexpectedResponse("could not find effects in transaction response".to_owned()))? - .created() .first() .ok_or_else(|| Error::TransactionUnexpectedResponse("no object was created in this transaction".to_owned()))? .object_id(); AuthenticatedAsset::get_by_id(created_asset_id, client) .await - .map(move |output| TransactionOutput { output, response }) + .map(move |output| TransactionOutputInternal { output, response }) } } @@ -489,35 +489,33 @@ pub struct TransferAssetTx { recipient: IotaAddress, } -#[async_trait] -impl Transaction for TransferAssetTx +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for TransferAssetTx where T: MoveType + Send + Sync, { type Output = TransferProposal; - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { - let tx = move_calls::asset::transfer::( + let tx = AssetMoveCallsAdapter::transfer::( self.asset.object_ref(client).await?, self.recipient, client.package_id(), )?; let tx_result = client.execute_transaction(tx, gas_budget).await?; - let created_obj_ids = tx_result - .effects - .as_ref() - .ok_or_else(|| Error::TransactionUnexpectedResponse("could not find effects in transaction response".to_owned()))? - .created() - .iter() - .map(|obj| obj.reference.object_id); + let effects_created = tx_result.effects_created().ok_or_else(|| { + Error::TransactionUnexpectedResponse("could not find effects in transaction response".to_owned()) + })?; + let created_obj_ids = effects_created.iter().map(|obj| obj.reference.object_id); for id in created_obj_ids { let object_type = client .read_api() @@ -531,7 +529,7 @@ where if object_type == TransferProposal::move_type(client.package_id()).to_string() { return TransferProposal::get_by_id(id, client) .await - .map(move |proposal| TransactionOutput { + .map(move |proposal| TransactionOutputInternal { output: proposal, response: tx_result, }); @@ -548,14 +546,15 @@ where #[derive(Debug)] pub struct AcceptTransferTx(TransferProposal); -#[async_trait] -impl Transaction for AcceptTransferTx { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for AcceptTransferTx { type Output = (); - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { @@ -576,7 +575,7 @@ impl Transaction for AcceptTransferTx { .initial_shared_version(client) .await .map_err(|e| Error::ObjectLookup(e.to_string()))?; - let tx = move_calls::asset::accept_proposal( + let tx = AssetMoveCallsAdapter::accept_proposal( (self.0.id(), initial_shared_version), cap, asset_ref, @@ -585,7 +584,7 @@ impl Transaction for AcceptTransferTx { )?; let response = client.execute_transaction(tx, gas_budget).await?; - Ok(TransactionOutput { output: (), response }) + Ok(TransactionOutputInternal { output: (), response }) } } @@ -593,14 +592,15 @@ impl Transaction for AcceptTransferTx { #[derive(Debug)] pub struct ConcludeTransferTx(TransferProposal); -#[async_trait] -impl Transaction for ConcludeTransferTx { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for ConcludeTransferTx { type Output = (); - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { @@ -616,7 +616,7 @@ impl Transaction for ConcludeTransferTx { .await .map_err(|e| Error::ObjectLookup(e.to_string()))?; - let tx = move_calls::asset::conclude_or_cancel( + let tx = AssetMoveCallsAdapter::conclude_or_cancel( (self.0.id(), initial_shared_version), cap, asset_ref, @@ -625,6 +625,6 @@ impl Transaction for ConcludeTransferTx { )?; let response = client.execute_transaction(tx, gas_budget).await?; - Ok(TransactionOutput { output: (), response }) + Ok(TransactionOutputInternal { output: (), response }) } } diff --git a/identity_iota_core/src/rebased/assets/public_available_vc.rs b/identity_iota_core/src/rebased/assets/public_available_vc.rs index f2cb5c8c4e..e110b592cf 100644 --- a/identity_iota_core/src/rebased/assets/public_available_vc.rs +++ b/identity_iota_core/src/rebased/assets/public_available_vc.rs @@ -2,64 +2,26 @@ // SPDX-License-Identifier: Apache-2.0 use std::ops::Deref; -use std::str::FromStr; use anyhow::Context as _; use identity_credential::credential::Credential; use identity_credential::credential::Jwt; use identity_credential::credential::JwtCredential; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaVerifiableCredential; use identity_jose::jwt::JwtHeader; use identity_jose::jwu; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Argument; -use iota_sdk::types::transaction::Command; -use iota_sdk::types::transaction::ProgrammableMoveCall; -use iota_sdk::types::TypeTag; use itertools::Itertools; -use move_core_types::ident_str; use secret_storage::Signer; -use serde::Deserialize; -use serde::Serialize; use crate::rebased::client::IdentityClient; use crate::rebased::client::IdentityClientReadOnly; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::transaction::Transaction; -use crate::rebased::utils::MoveType; -use crate::rebased::Error; +use crate::rebased::transaction::TransactionInternal; use super::AuthenticatedAsset; use super::AuthenticatedAssetBuilder; -#[derive(Debug, Clone, Serialize, Deserialize)] -struct IotaVerifiableCredential { - data: Vec, -} - -impl MoveType for IotaVerifiableCredential { - fn move_type(package: ObjectID) -> TypeTag { - TypeTag::from_str(&format!("{package}::public_vc::PublicVc")).expect("valid utf8") - } - - fn try_to_argument( - &self, - ptb: &mut ProgrammableTransactionBuilder, - package: Option, - ) -> Result { - let values = ptb - .pure(&self.data) - .map_err(|e| Error::InvalidArgument(e.to_string()))?; - Ok(ptb.command(Command::MoveCall(Box::new(ProgrammableMoveCall { - package: package.ok_or_else(|| Error::InvalidArgument("missing package ID".to_string()))?, - module: ident_str!("public_vc").into(), - function: ident_str!("new").into(), - type_arguments: vec![], - arguments: vec![values], - })))) - } -} - /// A publicly available verifiable credential. #[derive(Debug, Clone)] pub struct PublicAvailableVC { @@ -82,7 +44,7 @@ impl PublicAvailableVC { /// Get the JWT of the credential. pub fn jwt(&self) -> Jwt { - String::from_utf8(self.asset.content().data.clone()) + String::from_utf8(self.asset.content().data().clone()) .map(Jwt::new) .expect("JWT is valid UTF8") } @@ -97,12 +59,12 @@ impl PublicAvailableVC { { let jwt_bytes = String::from(jwt).into_bytes(); let credential = parse_jwt_credential(&jwt_bytes)?; - let asset = AuthenticatedAssetBuilder::new(IotaVerifiableCredential { data: jwt_bytes }) + let asset = AuthenticatedAssetBuilder::new(IotaVerifiableCredential::new(jwt_bytes)) .transferable(false) .mutable(true) .deletable(true) .finish() - .execute_with_opt_gas(gas_budget, client) + .execute_with_opt_gas_internal(gas_budget, client) .await? .output; @@ -122,7 +84,7 @@ impl PublicAvailableVC { } fn try_from_asset(asset: AuthenticatedAsset) -> Result { - let credential = parse_jwt_credential(&asset.content().data)?; + let credential = parse_jwt_credential(asset.content().data())?; Ok(Self { asset, credential }) } } diff --git a/identity_iota_core/src/rebased/client/full_client.rs b/identity_iota_core/src/rebased/client/full_client.rs index 66f174ae93..910f0e99fa 100644 --- a/identity_iota_core/src/rebased/client/full_client.rs +++ b/identity_iota_core/src/rebased/client/full_client.rs @@ -8,61 +8,43 @@ use crate::IotaDocument; use async_trait::async_trait; use fastcrypto::ed25519::Ed25519PublicKey; use fastcrypto::traits::ToFromBytes; +use identity_iota_interaction::move_types::language_storage::StructTag; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::IotaObjectDataFilter; +use identity_iota_interaction::rpc_types::IotaObjectResponseQuery; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectRef; use identity_verification::jwk::Jwk; -use iota_sdk::rpc_types::Coin; -use iota_sdk::rpc_types::IotaExecutionStatus; -use iota_sdk::rpc_types::IotaObjectData; -use iota_sdk::rpc_types::IotaObjectDataFilter; -use iota_sdk::rpc_types::IotaObjectResponseQuery; -use iota_sdk::rpc_types::IotaTransactionBlockEffects; -use iota_sdk::rpc_types::IotaTransactionBlockEffectsAPI; -use iota_sdk::rpc_types::IotaTransactionBlockEffectsV1; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::rpc_types::IotaTransactionBlockResponseOptions; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::crypto::DefaultHash; -use iota_sdk::types::crypto::Signature; -use iota_sdk::types::crypto::SignatureScheme; -use iota_sdk::types::quorum_driver_types::ExecuteTransactionRequestType; -use iota_sdk::types::transaction::ProgrammableTransaction; -use iota_sdk::types::transaction::Transaction; -use iota_sdk::types::transaction::TransactionData; -use move_core_types::language_storage::StructTag; -use secret_storage::SignatureScheme as SignatureSchemeT; use secret_storage::Signer; use serde::de::DeserializeOwned; use serde::Serialize; -use shared_crypto::intent::Intent; -use shared_crypto::intent::IntentMessage; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdaptedTraitObj; use crate::rebased::assets::AuthenticatedAssetBuilder; use crate::rebased::migration::Identity; use crate::rebased::migration::IdentityBuilder; -use crate::rebased::transaction::Transaction as TransactionT; -use crate::rebased::transaction::TransactionOutput; -use crate::rebased::utils::MoveType; +use crate::rebased::rebased_err; use crate::rebased::Error; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::MoveType; +use identity_iota_interaction::ProgrammableTransactionBcs; + +use crate::rebased::transaction::TransactionOutputInternal; +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + use crate::rebased::transaction::TransactionInternal as TransactionT; + type TransactionOutputT = TransactionOutputInternal; + } else { + use crate::rebased::transaction::TransactionInternal; + use crate::rebased::transaction::Transaction as TransactionT; + use crate::rebased::transaction::TransactionOutput as TransactionOutputT; + } +} use super::get_object_id_from_did; use super::IdentityClientReadOnly; -/// The minimum balance required to execute a transaction. -pub(crate) const MINIMUM_BALANCE: u64 = 1_000_000_000; - -/// A signature which is used to sign transactions. -pub struct IotaKeySignature { - /// The public key of the signature. - pub public_key: Vec, - /// The signature of the transaction. - pub signature: Vec, -} - -impl SignatureSchemeT for IotaKeySignature { - type PublicKey = Vec; - type Signature = Vec; -} - /// Mirrored types from identity_storage::KeyId #[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub struct KeyId(String); @@ -131,69 +113,28 @@ where }) } - async fn sign_transaction_data(&self, tx_data: &TransactionData) -> Result { - use fastcrypto::hash::HashFunction; - let sender_public_key = self.sender_public_key(); - - let intent = Intent::iota_transaction(); - let intent_msg = IntentMessage::new(intent, tx_data); - let mut hasher = DefaultHash::default(); - let bcs_bytes = bcs::to_bytes(&intent_msg).map_err(|err| { - Error::TransactionSigningFailed(format!("could not serialize transaction message to bcs; {err}")) - })?; - hasher.update(bcs_bytes); - let digest = hasher.finalize().digest; - - let raw_signature = self - .signer - .sign(&digest) - .await - .map_err(|err| Error::TransactionSigningFailed(format!("could not sign transaction message; {err}")))?; - - let binding = [ - [SignatureScheme::ED25519.flag()].as_slice(), - &raw_signature, - sender_public_key, - ] - .concat(); - let signature_bytes: &[u8] = binding.as_slice(); - - Signature::from_bytes(signature_bytes) - .map_err(|err| Error::TransactionSigningFailed(format!("could not parse signature to IOTA signature; {err}"))) - } - pub(crate) async fn execute_transaction( &self, - tx: ProgrammableTransaction, + tx_bcs: ProgrammableTransactionBcs, gas_budget: Option, - ) -> Result { - let gas_budget = match gas_budget { - Some(gas) => gas, - None => self.default_gas_budget(&tx).await?, - }; - let tx_data = self.get_transaction_data(tx, gas_budget).await?; - let signature = self.sign_transaction_data(&tx_data).await?; - - // execute tx - let response = self - .quorum_driver_api() - .execute_transaction_block( - Transaction::from_data(tx_data, vec![signature]), - IotaTransactionBlockResponseOptions::full_content(), - Some(ExecuteTransactionRequestType::WaitForLocalExecution), + ) -> Result { + // This code looks like we would call execute_transaction() on + // self.read_client (which is an IdentityClientReadOnly). + // Actually we call execute_transaction() on self.read_client.iota_client + // which is an IotaClientAdapter instance now, provided via the Deref trait. + // TODO: Find a more transparent way to reference the + // IotaClientAdapter for readonly. + self + .read_client + .execute_transaction( + self.sender_address(), + self.sender_public_key(), + tx_bcs, + gas_budget, + self.signer(), ) .await - .map_err(Error::TransactionExecutionFailed)?; - - if let Some(IotaTransactionBlockEffects::V1(IotaTransactionBlockEffectsV1 { - status: IotaExecutionStatus::Failure { error }, - .. - })) = &response.effects - { - Err(Error::TransactionUnexpectedResponse(error.to_string())) - } else { - Ok(response) - } + .map_err(rebased_err) } } @@ -226,93 +167,6 @@ impl IdentityClient { AuthenticatedAssetBuilder::new(content) } - pub(crate) async fn default_gas_budget(&self, tx: &ProgrammableTransaction) -> Result { - let gas_price = self - .read_api() - .get_reference_gas_price() - .await - .map_err(|e| Error::RpcError(e.to_string()))?; - let gas_coin = self.get_coin_for_transaction().await?; - let tx_data = TransactionData::new_programmable( - self.sender_address(), - vec![gas_coin.object_ref()], - tx.clone(), - 50_000_000, - gas_price, - ); - let dry_run_gas_result = self.read_api().dry_run_transaction_block(tx_data).await?.effects; - if dry_run_gas_result.status().is_err() { - let IotaExecutionStatus::Failure { error } = dry_run_gas_result.into_status() else { - unreachable!(); - }; - return Err(Error::TransactionUnexpectedResponse(error)); - } - let gas_summary = dry_run_gas_result.gas_cost_summary(); - let overhead = gas_price * 1000; - let net_used = gas_summary.net_gas_usage(); - let computation = gas_summary.computation_cost; - - let budget = overhead + (net_used.max(0) as u64).max(computation); - Ok(budget) - } - - async fn get_coin_for_transaction(&self) -> Result { - const LIMIT: usize = 10; - let mut cursor = None; - - loop { - let coins = self - .coin_read_api() - .get_coins(self.sender_address(), None, cursor, Some(LIMIT)) - .await?; - - let Some(coin) = coins.data.into_iter().max_by_key(|coin| coin.balance) else { - return Err(Error::GasIssue(format!( - "no coins found for address {}", - self.sender_address() - ))); - }; - - if coin.balance >= MINIMUM_BALANCE { - return Ok(coin); - } - - if !coins.has_next_page { - break; - } - - cursor = coins.next_cursor; - } - - Err(Error::GasIssue(format!( - "no coin found with minimum required balance of {} for address {}", - MINIMUM_BALANCE, - self.sender_address() - ))) - } - - async fn get_transaction_data( - &self, - programmable_transaction: ProgrammableTransaction, - gas_budget: u64, - ) -> Result { - let gas_price = self - .read_api() - .get_reference_gas_price() - .await - .map_err(|err| Error::GasIssue(format!("could not get gas price; {err}")))?; - let coin = self.get_coin_for_transaction().await?; - let tx_data = TransactionData::new_programmable( - self.sender_address(), - vec![coin.object_ref()], - programmable_transaction, - gas_budget, - gas_price, - ); - - Ok(tx_data) - } - /// Query the objects owned by the address wrapped by this client to find the object of type `tag` /// and that satisfies `predicate`. pub async fn find_owned_ref

(&self, tag: StructTag, predicate: P) -> Result, Error> @@ -422,29 +276,63 @@ pub fn convert_to_address(sender_public_key: &[u8]) -> Result( +impl PublishDidTx { + async fn execute_publish_did_tx_with_opt_gas( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { - let TransactionOutput { + let TransactionOutputInternal { output: identity, response, } = client .create_identity(self.0) .finish() - .execute_with_opt_gas(gas_budget, client) + .execute_with_opt_gas_internal(gas_budget, client) .await?; - Ok(TransactionOutput { + Ok(TransactionOutputInternal { output: identity.did_doc, response, }) } } + +// #[cfg(not(target_arch = "wasm32"))] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionT for PublishDidTx { + type Output = IotaDocument; + + #[cfg(not(target_arch = "wasm32"))] + async fn execute_with_opt_gas( + self, + gas_budget: Option, + client: &IdentityClient, + ) -> Result, Error> + where + S: Signer + Sync, + { + Ok( + self + .execute_publish_did_tx_with_opt_gas(gas_budget, client) + .await? + .into(), + ) + } + + #[cfg(target_arch = "wasm32")] + async fn execute_with_opt_gas_internal( + self, + gas_budget: Option, + client: &IdentityClient, + ) -> Result, Error> + where + S: Signer + Sync, + { + self.execute_publish_did_tx_with_opt_gas(gas_budget, client).await + } +} diff --git a/identity_iota_core/src/rebased/client/mod.rs b/identity_iota_core/src/rebased/client/mod.rs index 87cccc51ac..d5b4f76b57 100644 --- a/identity_iota_core/src/rebased/client/mod.rs +++ b/identity_iota_core/src/rebased/client/mod.rs @@ -6,3 +6,5 @@ mod read_only; pub use full_client::*; pub use read_only::*; + +pub use identity_iota_interaction::IotaKeySignature; diff --git a/identity_iota_core/src/rebased/client/read_only.rs b/identity_iota_core/src/rebased/client/read_only.rs index 0b38999e83..df2ee83083 100644 --- a/identity_iota_core/src/rebased/client/read_only.rs +++ b/identity_iota_core/src/rebased/client/read_only.rs @@ -14,42 +14,45 @@ use anyhow::anyhow; use anyhow::Context as _; use futures::stream::FuturesUnordered; +use crate::iota_interaction_adapter::IotaClientAdapter; +use crate::rebased::migration::get_alias; +use crate::rebased::migration::get_identity; +use crate::rebased::migration::lookup; +use crate::rebased::migration::Identity; +use crate::rebased::Error; use futures::StreamExt as _; use identity_core::common::Url; use identity_did::DID; -use iota_sdk::rpc_types::EventFilter; -use iota_sdk::rpc_types::IotaData as _; -use iota_sdk::rpc_types::IotaObjectData; -use iota_sdk::rpc_types::IotaObjectDataFilter; -use iota_sdk::rpc_types::IotaObjectDataOptions; -use iota_sdk::rpc_types::IotaObjectResponseQuery; -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::IotaClient; -use move_core_types::language_storage::StructTag; +use identity_iota_interaction::move_types::language_storage::StructTag; +use identity_iota_interaction::rpc_types::EventFilter; +use identity_iota_interaction::rpc_types::IotaData as _; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::IotaObjectDataFilter; +use identity_iota_interaction::rpc_types::IotaObjectDataOptions; +use identity_iota_interaction::rpc_types::IotaObjectResponseQuery; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::IotaClientTrait; use serde::de::DeserializeOwned; use serde::Deserialize; -use crate::rebased::migration::get_alias; -use crate::rebased::migration::get_identity; -use crate::rebased::migration::lookup; -use crate::rebased::migration::Identity; -use crate::rebased::Error; +#[cfg(not(target_arch = "wasm32"))] +use identity_iota_interaction::IotaClient; /// An [`IotaClient`] enriched with identity-related /// functionalities. #[derive(Clone)] pub struct IdentityClientReadOnly { - iota_client: IotaClient, + iota_client: IotaClientAdapter, iota_identity_pkg_id: ObjectID, migration_registry_id: ObjectID, network: NetworkName, } impl Deref for IdentityClientReadOnly { - type Target = IotaClient; + type Target = IotaClientAdapter; fn deref(&self) -> &Self::Target { &self.iota_client } @@ -74,16 +77,26 @@ impl IdentityClientReadOnly { self.migration_registry_id } - /// Attempts to create a new [`IdentityClientReadOnly`] from a given [`IotaClient`]. - /// - /// # Failures - /// This function fails if the provided `iota_client` is connected to an unrecognized - /// network. - /// - /// # Notes - /// When trying to connect to a local or unofficial network prefer using - /// [`IdentityClientReadOnly::new_with_pkg_id`]. - pub async fn new(iota_client: IotaClient) -> Result { + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + //TODO: Define fn new() for wasm32 platforms + } else { + /// Attempts to create a new [`IdentityClientReadOnly`] from a given [`IotaClient`]. + /// + /// # Failures + /// This function fails if the provided `iota_client` is connected to an unrecognized + /// network. + /// + /// # Notes + /// When trying to connect to a local or unofficial network prefer using + /// [`IdentityClientReadOnly::new_with_pkg_id`]. + pub async fn new(iota_client: IotaClient) -> Result { + Self::new_internal(IotaClientAdapter::new(iota_client)?).await + } + } + } + + async fn new_internal(iota_client: IotaClientAdapter) -> Result { let network = network_id(&iota_client).await?; let metadata = iota::well_known_networks::network_metadata(&network).ok_or_else(|| { Error::InvalidConfig(format!( @@ -103,9 +116,25 @@ impl IdentityClientReadOnly { }) } - /// Attempts to create a new [`IdentityClientReadOnly`] from - /// the given [`IotaClient`]. - pub async fn new_with_pkg_id(iota_client: IotaClient, iota_identity_pkg_id: ObjectID) -> Result { + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + //TODO: Define fn new() for wasm32 platforms + } else { + /// Attempts to create a new [`IdentityClientReadOnly`] from + /// the given [`IotaClient`]. + pub async fn new_with_pkg_id(iota_client: IotaClient, iota_identity_pkg_id: ObjectID) -> Result { + Self::new_with_pkg_id_internal( + IotaClientAdapter::new(iota_client)?, + iota_identity_pkg_id + ).await + } + } + } + + async fn new_with_pkg_id_internal( + iota_client: IotaClientAdapter, + iota_identity_pkg_id: ObjectID, + ) -> Result { let IdentityPkgMetadata { migration_registry_id, .. } = identity_pkg_metadata(&iota_client, iota_identity_pkg_id).await?; @@ -201,7 +230,15 @@ impl IdentityClientReadOnly { /// Resolves an [`Identity`] from its ID `object_id`. pub async fn get_identity(&self, object_id: ObjectID) -> Result { // spawn all checks - let all_futures = FuturesUnordered::, Error>> + Send>>>::new(); + cfg_if::cfg_if! { + // Unfortunately the compiler runs into lifetime problems if we try to use a 'type =' + // instead of the below ugly platform specific code + if #[cfg(target_arch = "wasm32")] { + let all_futures = FuturesUnordered::, Error>>>>>::new(); + } else { + let all_futures = FuturesUnordered::, Error>> + Send>>>::new(); + } + } all_futures.push(Box::pin(resolve_new(self, object_id))); all_futures.push(Box::pin(resolve_migrated(self, object_id))); all_futures.push(Box::pin(resolve_unmigrated(self, object_id))); @@ -214,7 +251,7 @@ impl IdentityClientReadOnly { } } -async fn network_id(iota_client: &IotaClient) -> Result { +async fn network_id(iota_client: &IotaClientAdapter) -> Result { let network_id = iota_client .read_api() .get_chain_identifier() @@ -237,7 +274,10 @@ struct MigrationRegistryCreatedEvent { // TODO: remove argument `package_id` and use `EventFilter::MoveEventField` to find the beacon event and thus the // package id. // TODO: authenticate the beacon event with though sender's ID. -async fn identity_pkg_metadata(iota_client: &IotaClient, package_id: ObjectID) -> Result { +async fn identity_pkg_metadata( + iota_client: &IotaClientAdapter, + package_id: ObjectID, +) -> Result { // const EVENT_BEACON_PATH: &str = "/beacon"; // const EVENT_BEACON_VALUE: &[u8] = b"identity.rs_pkg"; diff --git a/identity_iota_core/src/rebased/error.rs b/identity_iota_core/src/rebased/error.rs index c2fd24b516..9186b98596 100644 --- a/identity_iota_core/src/rebased/error.rs +++ b/identity_iota_core/src/rebased/error.rs @@ -3,13 +3,16 @@ //! Errors that may occur for the rebased logic. +#[cfg(target_arch = "wasm32")] +use iota_interaction_ts::error::TsSdkError; + /// This type represents all possible errors that can occur in the library. #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] #[non_exhaustive] pub enum Error { /// failed to connect to network. #[error("failed to connect to iota network node; {0:?}")] - Network(String, #[source] iota_sdk::error::Error), + Network(String, #[source] identity_iota_interaction::error::Error), /// could not lookup an object ID. #[error("failed to lookup an object; {0}")] ObjectLookup(String), @@ -39,7 +42,7 @@ pub enum Error { TransactionSigningFailed(String), /// Could not execute transaction. #[error("transaction execution failed; {0}")] - TransactionExecutionFailed(#[from] iota_sdk::error::Error), + TransactionExecutionFailed(#[from] identity_iota_interaction::error::Error), /// Transaction yielded invalid response. This usually means that the transaction was executed but did not produce /// the expected result. #[error("transaction returned an unexpected response; {0}")] @@ -68,4 +71,23 @@ pub enum Error { /// An error caused by a bcs serialization or deserialization. #[error("BCS error: {0}")] BcsError(#[from] bcs::Error), + /// An anyhow::error. + #[error("Any error: {0}")] + AnyError(#[from] anyhow::Error), + /// An error caused by a foreign function interface call. + #[error("FFI error: {0}")] + FfiError(String), + #[cfg(target_arch = "wasm32")] + /// An error originating from IOTA typescript SDK import bindings + #[error("TsSdkError: {0}")] + TsSdkError(#[from] TsSdkError), +} + +/// Can be used for example like `map_err(rebased_err)` to convert other error +/// types to identity_iota_core::rebased::Error. +pub fn rebased_err(error: T) -> Error +where + Error: From, +{ + error.into() } diff --git a/identity_iota_core/src/rebased/iota/mod.rs b/identity_iota_core/src/rebased/iota/mod.rs index fffb2b8c38..2a71e571af 100644 --- a/identity_iota_core/src/rebased/iota/mod.rs +++ b/identity_iota_core/src/rebased/iota/mod.rs @@ -1,6 +1,5 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -pub(crate) mod move_calls; pub(crate) mod types; pub(crate) mod well_known_networks; diff --git a/identity_iota_core/src/rebased/iota/move_calls/asset/create.rs b/identity_iota_core/src/rebased/iota/move_calls/asset/create.rs index 57397a4907..63c402debc 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/asset/create.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/asset/create.rs @@ -1,16 +1,17 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Command; -use iota_sdk::types::transaction::ProgrammableMoveCall; -use iota_sdk::types::transaction::ProgrammableTransaction; -use move_core_types::ident_str; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::Command; +use identity_iota_interaction::types::transaction::ProgrammableMoveCall; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; use serde::Serialize; -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use crate::rebased::Error; +use super::try_to_argument; pub(crate) fn new( inner: T, @@ -20,7 +21,7 @@ pub(crate) fn new( package: ObjectID, ) -> Result { let mut ptb = ProgrammableTransactionBuilder::new(); - let inner = inner.try_to_argument(&mut ptb, Some(package))?; + let inner = try_to_argument(&inner, &mut ptb, package)?; let mutable = ptb.pure(mutable).map_err(|e| Error::InvalidArgument(e.to_string()))?; let transferable = ptb .pure(transferable) diff --git a/identity_iota_core/src/rebased/iota/move_calls/asset/delete.rs b/identity_iota_core/src/rebased/iota/move_calls/asset/delete.rs index 715e06dbee..5a2adc0538 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/asset/delete.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/asset/delete.rs @@ -1,15 +1,15 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Command; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use move_core_types::ident_str; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::Command; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use crate::rebased::Error; pub(crate) fn delete(asset: ObjectRef, package: ObjectID) -> Result diff --git a/identity_iota_core/src/rebased/iota/move_calls/asset/mod.rs b/identity_iota_core/src/rebased/iota/move_calls/asset/mod.rs index 4a5d6683e3..dc0271ad8b 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/asset/mod.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/asset/mod.rs @@ -5,8 +5,10 @@ mod create; mod delete; mod transfer; mod update; +mod try_to_argument; pub(crate) use create::*; pub(crate) use delete::*; pub(crate) use transfer::*; pub(crate) use update::*; +pub(crate) use try_to_argument::try_to_argument; \ No newline at end of file diff --git a/identity_iota_core/src/rebased/iota/move_calls/asset/transfer.rs b/identity_iota_core/src/rebased/iota/move_calls/asset/transfer.rs index b5e54b7bd1..2505905a7c 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/asset/transfer.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/asset/transfer.rs @@ -1,18 +1,18 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::base_types::SequenceNumber; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Command; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use iota_sdk::types::TypeTag; -use move_core_types::ident_str; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::base_types::SequenceNumber; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::Command; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::ident_str; -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use crate::rebased::Error; pub(crate) fn transfer( diff --git a/identity_iota_core/src/rebased/iota/move_calls/asset/try_to_argument.rs b/identity_iota_core/src/rebased/iota/move_calls/asset/try_to_argument.rs new file mode 100644 index 0000000000..31ce4134d7 --- /dev/null +++ b/identity_iota_core/src/rebased/iota/move_calls/asset/try_to_argument.rs @@ -0,0 +1,33 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Serialize; +use identity_iota_interaction::{ident_str, MoveType, TypedValue}; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::transaction::{Argument, Command, ProgrammableMoveCall}; +use crate::rebased::Error; + +pub(crate) fn try_to_argument( + content: &T, + ptb: &mut ProgrammableTransactionBuilder, + package: ObjectID, +) -> Result { + match content.get_typed_value(package) { + TypedValue::IotaVerifiableCredential(value) => { + let values = ptb + .pure(value.data()) + .map_err(|e| Error::InvalidArgument(e.to_string()))?; + Ok(ptb.command(Command::MoveCall(Box::new(ProgrammableMoveCall { + package, + module: ident_str!("public_vc").into(), + function: ident_str!("new").into(), + type_arguments: vec![], + arguments: vec![values], + })))) + }, + TypedValue::Other(value) => { + ptb.pure(value).map_err(|e| Error::InvalidArgument(e.to_string())) + }, + } +} diff --git a/identity_iota_core/src/rebased/iota/move_calls/asset/update.rs b/identity_iota_core/src/rebased/iota/move_calls/asset/update.rs index 6e1e921a31..c94575e8b5 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/asset/update.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/asset/update.rs @@ -1,16 +1,16 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Command; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use move_core_types::ident_str; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::Command; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; use serde::Serialize; -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use crate::rebased::Error; pub(crate) fn update(asset: ObjectRef, new_content: T, package: ObjectID) -> Result diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/borrow_asset.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/borrow_asset.rs index 667e8de617..2071a5754c 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/borrow_asset.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/borrow_asset.rs @@ -3,21 +3,21 @@ use std::collections::HashMap; -use iota_sdk::rpc_types::IotaObjectData; -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::base_types::ObjectType; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Argument; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::base_types::ObjectType; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; use itertools::Itertools; -use move_core_types::ident_str; use crate::rebased::iota::move_calls::utils; use crate::rebased::proposals::BorrowAction; -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use super::ProposalContext; diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/config.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/config.rs index 60fb207c54..dd560718d2 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/config.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/config.rs @@ -4,15 +4,15 @@ use std::collections::HashSet; use std::str::FromStr; -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use iota_sdk::types::TypeTag; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::ident_str; use super::super::utils; diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/controller_execution.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/controller_execution.rs index d31dad53ea..efde7e4da6 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/controller_execution.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/controller_execution.rs @@ -1,18 +1,18 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Argument; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; use crate::rebased::iota::move_calls::utils; use crate::rebased::proposals::ControllerExecution; -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use super::ProposalContext; diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/create.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/create.rs index e14f0875e0..2d2d29b6c2 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/create.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/create.rs @@ -1,13 +1,13 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::ProgrammableTransaction; -use iota_sdk::types::TypeTag; -use iota_sdk::types::IOTA_FRAMEWORK_PACKAGE_ID; -use move_core_types::ident_str; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use identity_iota_interaction::ident_str; use crate::rebased::iota::move_calls::utils; use crate::rebased::Error; diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/deactivate.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/deactivate.rs index 42e3199f63..bb7ddb9189 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/deactivate.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/deactivate.rs @@ -1,13 +1,13 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; use crate::rebased::iota::move_calls::utils; diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/proposal.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/proposal.rs index 82e02e99d1..64b4d32e40 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/proposal.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/proposal.rs @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use crate::rebased::iota::move_calls::utils; -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use crate::rebased::Error; -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; pub(crate) fn approve( identity: OwnedObjectRef, diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/send_asset.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/send_asset.rs index e7b5311680..9fd39d6a9a 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/send_asset.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/send_asset.rs @@ -1,20 +1,20 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Argument; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use iota_sdk::types::TypeTag; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::ident_str; use crate::rebased::iota::move_calls; use crate::rebased::proposals::SendAction; -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use self::move_calls::utils; use super::ProposalContext; diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/update.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/update.rs index 4d642431a3..59f7560e08 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/update.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/update.rs @@ -1,13 +1,13 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; use crate::rebased::iota::move_calls::utils; diff --git a/identity_iota_core/src/rebased/iota/move_calls/identity/upgrade.rs b/identity_iota_core/src/rebased/iota/move_calls/identity/upgrade.rs index 33d9f1934f..636e0d0a14 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/identity/upgrade.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/identity/upgrade.rs @@ -1,13 +1,13 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::ident_str; use crate::rebased::iota::move_calls::utils; diff --git a/identity_iota_core/src/rebased/iota/move_calls/migration.rs b/identity_iota_core/src/rebased/iota/move_calls/migration.rs index f56f291ba3..6605ac0256 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/migration.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/migration.rs @@ -2,14 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 use super::utils; -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::transaction::ProgrammableTransaction; -use iota_sdk::types::IOTA_FRAMEWORK_PACKAGE_ID; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use identity_iota_interaction::ident_str; pub(crate) fn migrate_did_output( did_output: ObjectRef, diff --git a/identity_iota_core/src/rebased/iota/move_calls/utils.rs b/identity_iota_core/src/rebased/iota/move_calls/utils.rs index 0a1d380dc3..a186236876 100644 --- a/identity_iota_core/src/rebased/iota/move_calls/utils.rs +++ b/identity_iota_core/src/rebased/iota/move_calls/utils.rs @@ -1,19 +1,19 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::rebased::utils::MoveType; +use identity_iota_interaction::MoveType; use crate::rebased::Error; -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::STD_OPTION_MODULE_NAME; -use iota_sdk::types::object::Owner; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; -use iota_sdk::types::transaction::Argument; -use iota_sdk::types::transaction::ObjectArg; -use iota_sdk::types::IOTA_CLOCK_OBJECT_ID; -use iota_sdk::types::IOTA_CLOCK_OBJECT_SHARED_VERSION; -use iota_sdk::types::MOVE_STDLIB_PACKAGE_ID; -use move_core_types::ident_str; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::STD_OPTION_MODULE_NAME; +use identity_iota_interaction::types::object::Owner; +use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::transaction::ObjectArg; +use identity_iota_interaction::types::IOTA_CLOCK_OBJECT_ID; +use identity_iota_interaction::types::IOTA_CLOCK_OBJECT_SHARED_VERSION; +use identity_iota_interaction::types::MOVE_STDLIB_PACKAGE_ID; +use identity_iota_interaction::ident_str; use serde::Serialize; /// Adds a reference to the on-chain clock to `ptb`'s arguments. diff --git a/identity_iota_core/src/rebased/iota/types/mod.rs b/identity_iota_core/src/rebased/iota/types/mod.rs index 3937289194..4c68b07d71 100644 --- a/identity_iota_core/src/rebased/iota/types/mod.rs +++ b/identity_iota_core/src/rebased/iota/types/mod.rs @@ -3,8 +3,8 @@ mod number; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::id::UID; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::id::UID; pub(crate) use number::*; use serde::Deserialize; use serde::Serialize; diff --git a/identity_iota_core/src/rebased/iota/well_known_networks.rs b/identity_iota_core/src/rebased/iota/well_known_networks.rs index 706f4a2189..b0769a3845 100644 --- a/identity_iota_core/src/rebased/iota/well_known_networks.rs +++ b/identity_iota_core/src/rebased/iota/well_known_networks.rs @@ -1,7 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectID; use phf::phf_map; use phf::Map; @@ -71,7 +71,7 @@ impl IdentityNetworkMetadata { #[cfg(test)] mod test { - use iota_sdk::IotaClientBuilder; + use identity_iota_interaction::IotaClientBuilder; use crate::rebased::client::IdentityClientReadOnly; diff --git a/identity_iota_core/src/rebased/migration/alias.rs b/identity_iota_core/src/rebased/migration/alias.rs index 220d7e1a56..463286ab78 100644 --- a/identity_iota_core/src/rebased/migration/alias.rs +++ b/identity_iota_core/src/rebased/migration/alias.rs @@ -2,28 +2,29 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use iota_sdk::rpc_types::IotaExecutionStatus; -use iota_sdk::rpc_types::IotaObjectDataOptions; -use iota_sdk::rpc_types::IotaTransactionBlockEffectsAPI; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::id::UID; -use iota_sdk::types::transaction::ProgrammableTransaction; -use iota_sdk::types::TypeTag; -use iota_sdk::types::STARDUST_PACKAGE_ID; +use identity_iota_interaction::rpc_types::IotaExecutionStatus; +use identity_iota_interaction::rpc_types::IotaObjectDataOptions; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::id::UID; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::types::STARDUST_PACKAGE_ID; use secret_storage::Signer; use serde; use serde::Deserialize; use serde::Serialize; +use crate::iota_interaction_adapter::MigrationMoveCallsAdapter; use crate::rebased::client::IdentityClient; use crate::rebased::client::IdentityClientReadOnly; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; -use crate::rebased::transaction::Transaction; -use crate::rebased::transaction::TransactionOutput; -use crate::rebased::utils::MoveType; +use crate::rebased::transaction::TransactionInternal; +use crate::rebased::transaction::TransactionOutputInternal; use crate::rebased::Error; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::MigrationMoveCalls; +use identity_iota_interaction::MoveType; +use identity_iota_interaction::ProgrammableTransactionBcs; use super::get_identity; use super::Identity; @@ -69,13 +70,27 @@ pub async fn get_alias(client: &IdentityClientReadOnly, object_id: ObjectID) -> } } +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + // Add wasm32 compatible migrate() function wrapper here + } else { + use crate::rebased::transaction::Transaction; + impl UnmigratedAlias { + /// Returns a transaction that when executed migrates a legacy `Alias` + /// containing a DID Document to a new [`OnChainIdentity`]. + pub async fn migrate(self, client: &IdentityClientReadOnly) + -> Result, Error> { + self.migrate_internal(client).await + } + } + } +} + impl UnmigratedAlias { - /// Returns a transaction that when executed migrates a legacy `Alias` - /// containing a DID Document to a new [`OnChainIdentity`]. - pub async fn migrate( + pub(crate) async fn migrate_internal( self, client: &IdentityClientReadOnly, - ) -> Result, Error> { + ) -> Result, Error> { // Try to parse a StateMetadataDocument out of this alias. let identity = Identity::Legacy(self); let did_doc = identity.did_document(client.network())?; @@ -125,39 +140,45 @@ impl UnmigratedAlias { .map(|timestamp| timestamp.to_unix() as u64 * 1000); // Build migration tx. - let tx = - move_calls::migration::migrate_did_output(alias_output_ref, created, migration_registry_ref, client.package_id()) - .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; + let tx = MigrationMoveCallsAdapter::migrate_did_output( + alias_output_ref, + created, + migration_registry_ref, + client.package_id(), + ) + .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(MigrateLegacyAliasTx(tx)) } } #[derive(Debug)] -struct MigrateLegacyAliasTx(ProgrammableTransaction); +struct MigrateLegacyAliasTx(ProgrammableTransactionBcs); -#[async_trait] -impl Transaction for MigrateLegacyAliasTx { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for MigrateLegacyAliasTx { type Output = OnChainIdentity; - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { - let response = self.0.execute_with_opt_gas(gas_budget, client).await?.response; + let response = self.0.execute_with_opt_gas_internal(gas_budget, client).await?.response; // Make sure the tx was successful. - let effects = response - .effects - .as_ref() - .ok_or_else(|| Error::TransactionUnexpectedResponse("transaction had no effects".to_string()))?; - if let IotaExecutionStatus::Failure { error } = effects.status() { + let effects_execution_status = response + .effects_execution_status() + .ok_or_else(|| Error::TransactionUnexpectedResponse("transaction had no effects_execution_status".to_string()))?; + if let IotaExecutionStatus::Failure { error } = effects_execution_status { Err(Error::TransactionUnexpectedResponse(error.to_string())) } else { - let identity_ref = effects - .created() + let effects_created = response + .effects_created() + .ok_or_else(|| Error::TransactionUnexpectedResponse("transaction had no effects_created".to_string()))?; + let identity_ref = effects_created .iter() .find(|obj_ref| obj_ref.owner.is_shared()) .ok_or_else(|| { @@ -166,7 +187,7 @@ impl Transaction for MigrateLegacyAliasTx { get_identity(client, identity_ref.object_id()) .await - .map(move |identity| TransactionOutput { + .map(move |identity| TransactionOutputInternal { output: identity.expect("identity exists on-chain"), response, }) diff --git a/identity_iota_core/src/rebased/migration/identity.rs b/identity_iota_core/src/rebased/migration/identity.rs index 42556e92bb..7935413c82 100644 --- a/identity_iota_core/src/rebased/migration/identity.rs +++ b/identity_iota_core/src/rebased/migration/identity.rs @@ -6,6 +6,9 @@ use std::collections::HashSet; use std::ops::Deref; use std::str::FromStr; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use identity_iota_interaction::IdentityMoveCalls; + use crate::rebased::iota::types::Number; use crate::rebased::proposals::Upgrade; use crate::IotaDID; @@ -15,24 +18,21 @@ use crate::StateMetadataDocument; use crate::StateMetadataEncoding; use async_trait::async_trait; use identity_core::common::Timestamp; -use iota_sdk::rpc_types::IotaObjectData; -use iota_sdk::rpc_types::IotaObjectDataOptions; -use iota_sdk::rpc_types::IotaParsedData; -use iota_sdk::rpc_types::IotaParsedMoveObject; -use iota_sdk::rpc_types::IotaPastObjectResponse; -use iota_sdk::rpc_types::IotaTransactionBlockEffects; -use iota_sdk::rpc_types::IotaTransactionBlockResponseOptions; -use iota_sdk::rpc_types::ObjectChange; -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::base_types::SequenceNumber; -use iota_sdk::types::id::UID; -use iota_sdk::types::object::Owner; -use iota_sdk::types::TypeTag; -use move_core_types::ident_str; -use move_core_types::language_storage::StructTag; +use identity_iota_interaction::ident_str; +use identity_iota_interaction::move_types::language_storage::StructTag; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::IotaObjectDataOptions; +use identity_iota_interaction::rpc_types::IotaParsedData; +use identity_iota_interaction::rpc_types::IotaParsedMoveObject; +use identity_iota_interaction::rpc_types::IotaPastObjectResponse; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::id::UID; +use identity_iota_interaction::types::object::Owner; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::IotaKeySignature; use secret_storage::Signer; use serde; use serde::Deserialize; @@ -40,8 +40,6 @@ use serde::Serialize; use crate::rebased::client::IdentityClient; use crate::rebased::client::IdentityClientReadOnly; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use crate::rebased::proposals::BorrowAction; use crate::rebased::proposals::ConfigChange; use crate::rebased::proposals::ControllerExecution; @@ -49,10 +47,12 @@ use crate::rebased::proposals::DeactivateDid; use crate::rebased::proposals::ProposalBuilder; use crate::rebased::proposals::SendAction; use crate::rebased::proposals::UpdateDidDocument; -use crate::rebased::transaction::Transaction; -use crate::rebased::transaction::TransactionOutput; -use crate::rebased::utils::MoveType; +use crate::rebased::rebased_err; +use crate::rebased::transaction::TransactionInternal; +use crate::rebased::transaction::TransactionOutputInternal; use crate::rebased::Error; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::MoveType; use super::Multicontroller; use super::UnmigratedAlias; @@ -226,7 +226,7 @@ impl OnChainIdentity { } else { // no version given, this version will be included in history let version = identity_ref.version(); - let response = get_past_object(client, object_id, version).await?; + let response = client.get_past_object(object_id, version).await?; let latest_version = if let IotaPastObjectResponse::VersionFound(response_value) = response { response_value } else { @@ -265,86 +265,11 @@ pub fn has_previous_version(history_item: &IotaObjectData) -> Result Result { - client - .read_api() - .try_get_parsed_past_object(object_id, version, IotaObjectDataOptions::full_content()) - .await - .map_err(|err| { - Error::InvalidIdentityHistory(format!("could not look up object {object_id} version {version}; {err}")) - }) -} - async fn get_previous_version( client: &IdentityClientReadOnly, iod: IotaObjectData, ) -> Result, Error> { - // try to get digest of previous tx - // if we requested the prev tx and it isn't returned, this should be the oldest state - let prev_tx_digest = if let Some(value) = iod.previous_transaction { - value - } else { - return Ok(None); - }; - - // resolve previous tx - let prev_tx_response = client - .read_api() - .get_transaction_with_options( - prev_tx_digest, - IotaTransactionBlockResponseOptions::new().with_object_changes(), - ) - .await - .map_err(|err| { - Error::InvalidIdentityHistory(format!("could not get previous transaction {prev_tx_digest}; {err}")) - })?; - - // check for updated/created changes - let (created, other_changes): (Vec, _) = prev_tx_response - .clone() - .object_changes - .ok_or_else(|| { - Error::InvalidIdentityHistory(format!( - "could not find object changes for object {} in transaction {prev_tx_digest}", - iod.object_id - )) - })? - .into_iter() - .filter(|elem| iod.object_id.eq(&elem.object_id())) - .partition(|elem| matches!(elem, ObjectChange::Created { .. })); - - // previous tx contain create tx, so there is no previous version - if created.len() == 1 { - return Ok(None); - } - - let mut previous_versions: Vec = other_changes - .iter() - .filter_map(|elem| match elem { - ObjectChange::Mutated { previous_version, .. } => Some(*previous_version), - _ => None, - }) - .collect(); - - previous_versions.sort(); - - let earliest_previous = if let Some(value) = previous_versions.first() { - value - } else { - return Ok(None); // no mutations in prev tx, so no more versions can be found - }; - - let past_obj_response = get_past_object(client, iod.object_id, *earliest_previous).await?; - match past_obj_response { - IotaPastObjectResponse::VersionFound(value) => Ok(Some(value)), - _ => Err(Error::InvalidIdentityHistory(format!( - "could not find previous version, past object response: {past_obj_response:?}" - ))), - } + client.get_previous_version(iod).await.map_err(rebased_err) } /// Returns the [`OnChainIdentity`] having ID `object_id`, if it exists. @@ -517,14 +442,15 @@ impl MoveType for OnChainIdentity { #[derive(Debug)] pub struct CreateIdentityTx(IdentityBuilder); -#[async_trait] -impl Transaction for CreateIdentityTx { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for CreateIdentityTx { type Output = OnChainIdentity; - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { @@ -537,7 +463,7 @@ impl Transaction for CreateIdentityTx { .pack(StateMetadataEncoding::default()) .map_err(|e| Error::DidDocSerialization(e.to_string()))?; let programmable_transaction = if controllers.is_empty() { - move_calls::identity::new(&did_doc, client.package_id())? + IdentityMoveCallsAdapter::new_identity(&did_doc, client.package_id())? } else { let threshold = match threshold { Some(t) => t, @@ -551,19 +477,14 @@ impl Transaction for CreateIdentityTx { )) } }; - move_calls::identity::new_with_controllers(&did_doc, controllers, threshold, client.package_id())? + IdentityMoveCallsAdapter::new_with_controllers(&did_doc, controllers, threshold, client.package_id())? }; let response = client.execute_transaction(programmable_transaction, gas_budget).await?; - let created = match response.clone().effects { - Some(IotaTransactionBlockEffects::V1(effects)) => effects.created, - _ => { - return Err(Error::TransactionUnexpectedResponse(format!( - "could not find effects in transaction response: {response:?}" - ))); - } - }; + let created = response.effects_created().ok_or_else(|| { + Error::TransactionUnexpectedResponse("could not find effects_created in transaction".to_string()) + })?; let new_identities: Vec = created .into_iter() .filter(|elem| { @@ -579,7 +500,8 @@ impl Transaction for CreateIdentityTx { [value] => value.object_id(), _ => { return Err(Error::TransactionUnexpectedResponse(format!( - "could not find new identity in response: {response:?}" + "could not find new identity in response: {}", + response.to_string() ))); } }; @@ -587,7 +509,7 @@ impl Transaction for CreateIdentityTx { get_identity(client, new_identity_id) .await .and_then(|identity| identity.ok_or_else(|| Error::ObjectLookup(new_identity_id.to_string()))) - .map(move |identity| TransactionOutput { + .map(move |identity| TransactionOutputInternal { output: identity, response, }) diff --git a/identity_iota_core/src/rebased/migration/multicontroller.rs b/identity_iota_core/src/rebased/migration/multicontroller.rs index 141a764149..a1d75ad71e 100644 --- a/identity_iota_core/src/rebased/migration/multicontroller.rs +++ b/identity_iota_core/src/rebased/migration/multicontroller.rs @@ -6,11 +6,11 @@ use std::collections::HashSet; use crate::rebased::iota::types::Bag; use crate::rebased::iota::types::Number; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::collection_types::Entry; -use iota_sdk::types::collection_types::VecMap; -use iota_sdk::types::collection_types::VecSet; -use iota_sdk::types::id::UID; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::collection_types::Entry; +use identity_iota_interaction::types::collection_types::VecMap; +use identity_iota_interaction::types::collection_types::VecSet; +use identity_iota_interaction::types::id::UID; use serde::Deserialize; use serde::Serialize; diff --git a/identity_iota_core/src/rebased/migration/registry.rs b/identity_iota_core/src/rebased/migration/registry.rs index 28cc9a1d74..196cf15e16 100644 --- a/identity_iota_core/src/rebased/migration/registry.rs +++ b/identity_iota_core/src/rebased/migration/registry.rs @@ -1,9 +1,10 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::rpc_types::IotaData; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::id::ID; +use identity_iota_interaction::rpc_types::IotaData; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::id::ID; +use identity_iota_interaction::IotaClientTrait; use crate::rebased::client::IdentityClientReadOnly; @@ -26,19 +27,16 @@ pub enum Error { /// Lookup a legacy `alias_id` into the migration registry /// to get the UID of the corresponding migrated DID document if any. -pub async fn lookup( - iota_client: &IdentityClientReadOnly, - alias_id: ObjectID, -) -> Result, Error> { +pub async fn lookup(id_client: &IdentityClientReadOnly, alias_id: ObjectID) -> Result, Error> { let dynamic_field_name = serde_json::from_value(serde_json::json!({ "type": "0x2::object::ID", "value": alias_id.to_string() })) .expect("valid move value"); - let identity_id = iota_client + let identity_id = id_client .read_api() - .get_dynamic_field_object(iota_client.migration_registry_id(), dynamic_field_name) + .get_dynamic_field_object(id_client.migration_registry_id(), dynamic_field_name) .await .map_err(|e| Error::Client(e.into()))? .data @@ -55,7 +53,7 @@ pub async fn lookup( .transpose()?; if let Some(id) = identity_id { - get_identity(iota_client, id).await.map_err(|e| Error::Client(e.into())) + get_identity(id_client, id).await.map_err(|e| Error::Client(e.into())) } else { Ok(None) } diff --git a/identity_iota_core/src/rebased/mod.rs b/identity_iota_core/src/rebased/mod.rs index c1745daa67..9dfd1f9139 100644 --- a/identity_iota_core/src/rebased/mod.rs +++ b/identity_iota_core/src/rebased/mod.rs @@ -17,4 +17,4 @@ pub mod transaction; pub mod utils; pub use assets::*; -pub use error::Error; +pub use error::*; diff --git a/identity_iota_core/src/rebased/proposals/borrow.rs b/identity_iota_core/src/rebased/proposals/borrow.rs index 9f0bc7cc9f..02cb94b97f 100644 --- a/identity_iota_core/src/rebased/proposals/borrow.rs +++ b/identity_iota_core/src/rebased/proposals/borrow.rs @@ -4,22 +4,26 @@ use std::collections::HashMap; use std::marker::PhantomData; +use crate::iota_interaction_adapter::AdapterError; +use crate::iota_interaction_adapter::AdapterNativeResponse; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; + use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use crate::rebased::migration::Proposal; use crate::rebased::transaction::ProtoTransaction; -use crate::rebased::transaction::Transaction; -use crate::rebased::transaction::TransactionOutput; -use crate::rebased::utils::MoveType; +use crate::rebased::transaction::TransactionInternal; +use crate::rebased::transaction::TransactionOutputInternal; use crate::rebased::Error; use async_trait::async_trait; -use iota_sdk::rpc_types::IotaObjectData; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; -use iota_sdk::types::transaction::Argument; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::MoveType; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; @@ -31,11 +35,32 @@ use super::ProposalBuilder; use super::ProposalT; use super::UserDrivenTx; -pub(crate) type IntentFn = Box) + Send>; +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + use iota_interaction_ts::NativeTsTransactionBuilderBindingWrapper as Ptb; + use iota_interaction_ts::error::TsSdkError as IotaInteractionError; + /// Instances of BorrowIntentFnT can be used as user-provided function to describe how + /// a borrowed assets shall be used. + pub trait BorrowIntentFnT: FnOnce(&mut Ptb, &HashMap) {} + impl BorrowIntentFnT for T where T: FnOnce(&mut Ptb, &HashMap) {} + /// Boxed dynamic trait object of {@link BorrowIntentFnT} + #[allow(unreachable_pub)] + pub type BorrowIntentFn = Box; + } else { + use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; + /// Instances of BorrowIntentFnT can be used as user-provided function to describe how + /// a borrowed assets shall be used. + pub trait BorrowIntentFnT: FnOnce(&mut Ptb, &HashMap) {} + impl BorrowIntentFnT for T where T: FnOnce(&mut Ptb, &HashMap) {} + /// Boxed dynamic trait object of {@link BorrowIntentFnT} + #[allow(unreachable_pub)] + pub type BorrowIntentFn = Box; + } +} /// Action used to borrow in transaction [OnChainIdentity]'s assets. #[derive(Deserialize, Serialize)] -pub struct BorrowAction { +pub struct BorrowAction { objects: Vec, #[serde(skip, default = "Option::default")] intent_fn: Option, @@ -54,7 +79,7 @@ impl Default for BorrowAction { /// the borrowed assets shall be used. pub struct BorrowActionWithIntent(BorrowAction) where - F: FnOnce(&mut Ptb, &HashMap); + F: BorrowIntentFnT; impl MoveType for BorrowAction { fn move_type(package: ObjectID) -> TypeTag { @@ -114,13 +139,15 @@ impl<'i, F> ProposalBuilder<'i, BorrowAction> { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProposalT for Proposal> where - F: FnOnce(&mut Ptb, &HashMap) + Send, + F: BorrowIntentFnT + Send, { type Action = BorrowAction; type Output = (); + type Response = IotaTransactionBlockResponseAdapter; async fn create<'i, S>( action: Self::Action, @@ -153,7 +180,7 @@ where } object_data_list }; - move_calls::identity::create_and_execute_borrow( + IdentityMoveCallsAdapter::create_and_execute_borrow( identity_ref, controller_cap_ref, object_data_list, @@ -162,7 +189,7 @@ where client.package_id(), ) } else { - move_calls::identity::propose_borrow( + IdentityMoveCallsAdapter::propose_borrow( identity_ref, controller_cap_ref, action.objects, @@ -198,7 +225,9 @@ where }) } - fn parse_tx_effects(_tx_response: &IotaTransactionBlockResponse) -> Result { + fn parse_tx_effects_internal( + _tx_response: &dyn IotaTransactionBlockResponseT, + ) -> Result { Ok(()) } } @@ -207,7 +236,7 @@ impl<'i, F> UserDrivenTx<'i, BorrowAction> { /// Defines how the borrowed assets should be used. pub fn with_intent(self, intent_fn: F1) -> UserDrivenTx<'i, BorrowActionWithIntent> where - F1: FnOnce(&mut Ptb, &HashMap), + F1: BorrowIntentFnT, { let UserDrivenTx { identity, @@ -224,25 +253,26 @@ impl<'i, F> UserDrivenTx<'i, BorrowAction> { } impl<'i, F> ProtoTransaction for UserDrivenTx<'i, BorrowAction> { - type Input = IntentFn; - type Tx = UserDrivenTx<'i, BorrowActionWithIntent>; + type Input = BorrowIntentFn; + type Tx = UserDrivenTx<'i, BorrowActionWithIntent>; fn with(self, input: Self::Input) -> Self::Tx { self.with_intent(input) } } -#[async_trait] -impl Transaction for UserDrivenTx<'_, BorrowActionWithIntent> +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for UserDrivenTx<'_, BorrowActionWithIntent> where - F: FnOnce(&mut Ptb, &HashMap) + Send, + F: BorrowIntentFnT + Send, { type Output = (); - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { @@ -269,7 +299,7 @@ where object_data_list }; - let tx = move_calls::identity::execute_borrow( + let tx = IdentityMoveCallsAdapter::execute_borrow( identity_ref, controller_cap_ref, proposal_id, @@ -287,7 +317,7 @@ where tx, _action: PhantomData::, } - .execute_with_opt_gas(gas_budget, client) + .execute_with_opt_gas_internal(gas_budget, client) .await } } diff --git a/identity_iota_core/src/rebased/proposals/config_change.rs b/identity_iota_core/src/rebased/proposals/config_change.rs index afe3917a5f..57f76ab5f9 100644 --- a/identity_iota_core/src/rebased/proposals/config_change.rs +++ b/identity_iota_core/src/rebased/proposals/config_change.rs @@ -7,25 +7,31 @@ use std::marker::PhantomData; use std::ops::DerefMut as _; use std::str::FromStr as _; +use crate::iota_interaction_adapter::AdapterError; +use crate::iota_interaction_adapter::AdapterNativeResponse; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::OptionalSync; + use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use crate::rebased::migration::Proposal; use async_trait::async_trait; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::collection_types::Entry; -use iota_sdk::types::collection_types::VecMap; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::collection_types::Entry; +use identity_iota_interaction::types::collection_types::VecMap; +use identity_iota_interaction::types::TypeTag; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; use crate::rebased::iota::types::Number; use crate::rebased::migration::OnChainIdentity; -use crate::rebased::utils::MoveType; use crate::rebased::Error; +use identity_iota_interaction::MoveType; use super::CreateProposalTx; use super::ExecuteProposalTx; @@ -174,10 +180,12 @@ impl ConfigChange { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProposalT for Proposal { type Action = ConfigChange; type Output = (); + type Response = IotaTransactionBlockResponseAdapter; async fn create<'i, S>( action: Self::Action, @@ -186,7 +194,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { // Check the validity of the proposed changes. action.validate(identity)?; @@ -200,7 +208,7 @@ impl ProposalT for Proposal { .controller_voting_power(controller_cap_ref.0) .expect("controller exists"); let chained_execution = sender_vp >= identity.threshold(); - let tx = move_calls::identity::propose_config_change( + let tx = IdentityMoveCallsAdapter::propose_config_change( identity_ref, controller_cap_ref, expiration, @@ -226,7 +234,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let proposal_id = self.id(); let identity_ref = client @@ -235,9 +243,13 @@ impl ProposalT for Proposal { .expect("identity exists on-chain"); let controller_cap_ref = identity.get_controller_cap(client).await?; - let tx = - move_calls::identity::execute_config_change(identity_ref, controller_cap_ref, proposal_id, client.package_id()) - .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; + let tx = IdentityMoveCallsAdapter::execute_config_change( + identity_ref, + controller_cap_ref, + proposal_id, + client.package_id(), + ) + .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(ExecuteProposalTx { identity, @@ -246,7 +258,9 @@ impl ProposalT for Proposal { }) } - fn parse_tx_effects(_tx_response: &IotaTransactionBlockResponse) -> Result { + fn parse_tx_effects_internal( + _tx_response: &dyn IotaTransactionBlockResponseT, + ) -> Result { Ok(()) } } diff --git a/identity_iota_core/src/rebased/proposals/controller.rs b/identity_iota_core/src/rebased/proposals/controller.rs index 920bc6da99..0296ec8d24 100644 --- a/identity_iota_core/src/rebased/proposals/controller.rs +++ b/identity_iota_core/src/rebased/proposals/controller.rs @@ -3,24 +3,28 @@ use std::marker::PhantomData; +use crate::iota_interaction_adapter::AdapterError; +use crate::iota_interaction_adapter::AdapterNativeResponse; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; + use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use crate::rebased::migration::Proposal; use crate::rebased::transaction::ProtoTransaction; -use crate::rebased::transaction::Transaction; -use crate::rebased::transaction::TransactionOutput; -use crate::rebased::utils::MoveType; +use crate::rebased::transaction::TransactionInternal; +use crate::rebased::transaction::TransactionOutputInternal; use crate::rebased::Error; use async_trait::async_trait; -use iota_sdk::rpc_types::IotaObjectRef; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::rpc_types::OwnedObjectRef; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; -use iota_sdk::types::transaction::Argument; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::rpc_types::IotaObjectRef; +use identity_iota_interaction::rpc_types::OwnedObjectRef; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::transaction::Argument; +use identity_iota_interaction::types::TypeTag; +use identity_iota_interaction::MoveType; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; @@ -32,12 +36,32 @@ use super::ProposalBuilder; use super::ProposalT; use super::UserDrivenTx; -pub(crate) type IntentFn = Box; +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + use iota_interaction_ts::NativeTsTransactionBuilderBindingWrapper as Ptb; + /// Instances of ControllerIntentFnT can be used as user-provided function to describe how + /// a borrowed identity's controller capability will be used. + pub trait ControllerIntentFnT: FnOnce(&mut Ptb, &Argument) {} + impl ControllerIntentFnT for T where T: FnOnce(&mut Ptb, &Argument) {} + #[allow(unreachable_pub)] + /// Boxed dynamic trait object of {@link ControllerIntentFnT} + pub type ControllerIntentFn = Box; + } else { + use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; + /// Instances of ControllerIntentFnT can be used as user-provided function to describe how + /// a borrowed identity's controller capability will be used. + pub trait ControllerIntentFnT: FnOnce(&mut Ptb, &Argument) {} + impl ControllerIntentFnT for T where T: FnOnce(&mut Ptb, &Argument) {} + #[allow(unreachable_pub)] + /// Boxed dynamic trait object of {@link ControllerIntentFnT} + pub type ControllerIntentFn = Box; + } +} /// Borrow an [`OnChainIdentity`]'s controller capability to exert control on /// a sub-owned identity. #[derive(Debug, Deserialize, Serialize)] -pub struct ControllerExecution { +pub struct ControllerExecution { controller_cap: ObjectID, identity: IotaAddress, #[serde(skip, default = "Option::default")] @@ -52,7 +76,7 @@ where impl ControllerExecutionWithIntent where - F: FnOnce(&mut Ptb, &Argument), + F: ControllerIntentFnT, { fn new(action: ControllerExecution) -> Self { debug_assert!(action.intent_fn.is_some()); @@ -120,13 +144,15 @@ impl MoveType for ControllerExecution { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProposalT for Proposal> where - F: FnOnce(&mut Ptb, &Argument) + Send, + F: ControllerIntentFnT + Send, { type Action = ControllerExecution; type Output = (); + type Response = IotaTransactionBlockResponseAdapter; async fn create<'i, S>( action: Self::Action, @@ -162,7 +188,7 @@ where }) .ok_or_else(|| Error::ObjectLookup(format!("object {} doesn't exist", action.controller_cap)))?; - move_calls::identity::create_and_execute_controller_execution( + IdentityMoveCallsAdapter::create_and_execute_controller_execution( identity_ref, controller_cap_ref, expiration, @@ -171,7 +197,7 @@ where client.package_id(), ) } else { - move_calls::identity::propose_controller_execution( + IdentityMoveCallsAdapter::propose_controller_execution( identity_ref, controller_cap_ref, action.controller_cap, @@ -207,7 +233,9 @@ where }) } - fn parse_tx_effects(_tx_response: &IotaTransactionBlockResponse) -> Result { + fn parse_tx_effects_internal( + _tx_response: &dyn IotaTransactionBlockResponseT, + ) -> Result { Ok(()) } } @@ -216,7 +244,7 @@ impl<'i, F> UserDrivenTx<'i, ControllerExecution> { /// Defines how the borrowed assets should be used. pub fn with_intent(self, intent_fn: F1) -> UserDrivenTx<'i, ControllerExecutionWithIntent> where - F1: FnOnce(&mut Ptb, &Argument), + F1: ControllerIntentFnT, { let UserDrivenTx { identity, @@ -233,25 +261,26 @@ impl<'i, F> UserDrivenTx<'i, ControllerExecution> { } impl<'i, F> ProtoTransaction for UserDrivenTx<'i, ControllerExecution> { - type Input = IntentFn; - type Tx = UserDrivenTx<'i, ControllerExecutionWithIntent>; + type Input = ControllerIntentFn; + type Tx = UserDrivenTx<'i, ControllerExecutionWithIntent>; fn with(self, input: Self::Input) -> Self::Tx { self.with_intent(input) } } -#[async_trait] -impl Transaction for UserDrivenTx<'_, ControllerExecutionWithIntent> +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for UserDrivenTx<'_, ControllerExecutionWithIntent> where - F: FnOnce(&mut Ptb, &Argument) + Send, + F: ControllerIntentFnT + Send, { type Output = (); - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { @@ -280,7 +309,7 @@ where }) .ok_or_else(|| Error::ObjectLookup(format!("object {borrowing_cap_id} doesn't exist")))?; - let tx = move_calls::identity::execute_controller_execution( + let tx = IdentityMoveCallsAdapter::execute_controller_execution( identity_ref, controller_cap_ref, proposal_id, @@ -298,7 +327,7 @@ where tx, _action: PhantomData::, } - .execute_with_opt_gas(gas_budget, client) + .execute_with_opt_gas_internal(gas_budget, client) .await } } diff --git a/identity_iota_core/src/rebased/proposals/deactivate_did.rs b/identity_iota_core/src/rebased/proposals/deactivate_did.rs index 72cac11264..f72ddd043d 100644 --- a/identity_iota_core/src/rebased/proposals/deactivate_did.rs +++ b/identity_iota_core/src/rebased/proposals/deactivate_did.rs @@ -3,21 +3,27 @@ use std::marker::PhantomData; +use crate::iota_interaction_adapter::AdapterError; +use crate::iota_interaction_adapter::AdapterNativeResponse; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::OptionalSync; + use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use async_trait::async_trait; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::TypeTag; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; use crate::rebased::migration::OnChainIdentity; use crate::rebased::migration::Proposal; -use crate::rebased::utils::MoveType; use crate::rebased::Error; +use identity_iota_interaction::MoveType; use super::CreateProposalTx; use super::ExecuteProposalTx; @@ -42,10 +48,12 @@ impl MoveType for DeactivateDid { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProposalT for Proposal { type Action = DeactivateDid; type Output = (); + type Response = IotaTransactionBlockResponseAdapter; async fn create<'i, S>( _action: Self::Action, @@ -54,7 +62,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let identity_ref = client .get_object_ref_by_id(identity.id()) @@ -66,7 +74,7 @@ impl ProposalT for Proposal { .expect("controller exists"); let chained_execution = sender_vp >= identity.threshold(); let tx = - move_calls::identity::propose_deactivation(identity_ref, controller_cap_ref, expiration, client.package_id()) + IdentityMoveCallsAdapter::propose_deactivation(identity_ref, controller_cap_ref, expiration, client.package_id()) .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(CreateProposalTx { @@ -83,7 +91,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let proposal_id = self.id(); let identity_ref = client @@ -92,9 +100,13 @@ impl ProposalT for Proposal { .expect("identity exists on-chain"); let controller_cap_ref = identity.get_controller_cap(client).await?; - let tx = - move_calls::identity::execute_deactivation(identity_ref, controller_cap_ref, proposal_id, client.package_id()) - .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; + let tx = IdentityMoveCallsAdapter::execute_deactivation( + identity_ref, + controller_cap_ref, + proposal_id, + client.package_id(), + ) + .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(ExecuteProposalTx { identity, @@ -103,7 +115,9 @@ impl ProposalT for Proposal { }) } - fn parse_tx_effects(_tx_response: &IotaTransactionBlockResponse) -> Result { + fn parse_tx_effects_internal( + _tx_response: &dyn IotaTransactionBlockResponseT, + ) -> Result { Ok(()) } } diff --git a/identity_iota_core/src/rebased/proposals/mod.rs b/identity_iota_core/src/rebased/proposals/mod.rs index f0bef48355..cfabad0b1b 100644 --- a/identity_iota_core/src/rebased/proposals/mod.rs +++ b/identity_iota_core/src/rebased/proposals/mod.rs @@ -13,26 +13,41 @@ use std::marker::PhantomData; use std::ops::Deref; use std::ops::DerefMut; +cfg_if::cfg_if! { + if #[cfg(not(target_arch = "wasm32"))] { + use identity_iota_interaction::rpc_types::IotaTransactionBlockResponse; + use crate::rebased::transaction::Transaction; + } +} +use crate::iota_interaction_adapter::AdapterError; +use crate::iota_interaction_adapter::AdapterNativeResponse; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; + +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::OptionalSend; +use identity_iota_interaction::ProgrammableTransactionBcs; + use crate::rebased::client::IdentityClientReadOnly; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use crate::rebased::migration::get_identity; use crate::rebased::transaction::ProtoTransaction; +use crate::rebased::transaction::TransactionInternal; +use crate::rebased::transaction::TransactionOutputInternal; use async_trait::async_trait; pub use borrow::*; pub use config_change::*; pub use controller::*; pub use deactivate_did::*; -use iota_sdk::rpc_types::IotaExecutionStatus; -use iota_sdk::rpc_types::IotaObjectData; -use iota_sdk::rpc_types::IotaObjectDataOptions; -use iota_sdk::rpc_types::IotaTransactionBlockEffectsAPI; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::base_types::ObjectRef; -use iota_sdk::types::base_types::ObjectType; -use iota_sdk::types::transaction::ProgrammableTransaction; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::rpc_types::IotaExecutionStatus; +use identity_iota_interaction::rpc_types::IotaObjectData; +use identity_iota_interaction::rpc_types::IotaObjectDataOptions; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::types::base_types::ObjectType; +use identity_iota_interaction::types::TypeTag; use secret_storage::Signer; pub use send::*; use serde::de::DeserializeOwned; @@ -42,18 +57,31 @@ pub use upgrade::*; use crate::rebased::client::IdentityClient; use crate::rebased::migration::OnChainIdentity; use crate::rebased::migration::Proposal; -use crate::rebased::transaction::Transaction; -use crate::rebased::transaction::TransactionOutput; -use crate::rebased::utils::MoveType; use crate::rebased::Error; +use identity_iota_interaction::MoveType; + +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + /// The internally used [`Transaction`] resulting from a proposal + pub trait ResultingTransactionT: TransactionInternal {} + impl ResultingTransactionT for T where T: TransactionInternal {} + } else { + /// The [`Transaction`] resulting from a proposal + pub trait ResultingTransactionT: Transaction {} + impl ResultingTransactionT for T where T: Transaction {} + } +} /// Interface that allows the creation and execution of an [`OnChainIdentity`]'s [`Proposal`]s. -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait ProposalT: Sized { /// The [`Proposal`] action's type. type Action; /// The output of the [`Proposal`] type Output; + /// Platform-agnostic type of the IotaTransactionBlockResponse + type Response: IotaTransactionBlockResponseT; /// Creates a new [`Proposal`] with the provided action and expiration. async fn create<'i, S>( @@ -61,7 +89,7 @@ pub trait ProposalT: Sized { expiration: Option, identity: &'i mut OnChainIdentity, client: &IdentityClient, - ) -> Result>, Error> + ) -> Result>, Error> where S: Signer + Sync; @@ -74,8 +102,17 @@ pub trait ProposalT: Sized { where S: Signer + Sync; + #[cfg(not(target_arch = "wasm32"))] /// Parses the transaction's effects and returns the output of the [`Proposal`]. - fn parse_tx_effects(tx_response: &IotaTransactionBlockResponse) -> Result; + fn parse_tx_effects(tx_response: &IotaTransactionBlockResponse) -> Result { + let adapter = IotaTransactionBlockResponseAdapter::new(tx_response.clone()); + Self::parse_tx_effects_internal(&adapter) + } + + /// For internal platform-agnostic usage only. + fn parse_tx_effects_internal( + tx_response: &dyn IotaTransactionBlockResponseT, + ) -> Result; } impl Proposal { @@ -129,7 +166,7 @@ impl<'i, A> ProposalBuilder<'i, A> { pub async fn finish<'c, S>( self, client: &'c IdentityClient, - ) -> Result>> + use<'i, 'c, S, A>, Error> + ) -> Result>> + use<'i, 'c, S, A>, Error> where Proposal: ProposalT, S: Signer + Sync, @@ -160,24 +197,25 @@ pub enum ProposalResult { #[derive(Debug)] pub struct CreateProposalTx<'i, A> { identity: &'i mut OnChainIdentity, - tx: ProgrammableTransaction, + tx: ProgrammableTransactionBcs, chained_execution: bool, _action: PhantomData, } -#[async_trait] -impl Transaction for CreateProposalTx<'_, A> +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for CreateProposalTx<'_, A> where Proposal: ProposalT + DeserializeOwned, A: Send, { type Output = ProposalResult>; - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result>>, Error> + ) -> Result>>, Error> where S: Signer + Sync, { @@ -188,13 +226,11 @@ where .. } = self; let tx_response = client.execute_transaction(tx, gas_budget).await?; - let tx_effects_status = tx_response - .effects - .as_ref() - .ok_or_else(|| Error::TransactionUnexpectedResponse("missing transaction's effects".to_string()))? - .status(); + let tx_effects_execution_status = tx_response + .effects_execution_status() + .ok_or_else(|| Error::TransactionUnexpectedResponse("missing transaction's effects".to_string()))?; - if let IotaExecutionStatus::Failure { error } = tx_effects_status { + if let IotaExecutionStatus::Failure { error } = tx_effects_execution_status { return Err(Error::TransactionUnexpectedResponse(error.clone())); } @@ -206,16 +242,14 @@ where if chained_execution { // The proposal has been created and executed right-away. Parse its effects. - Proposal::::parse_tx_effects(&tx_response).map(ProposalResult::Executed) + Proposal::::parse_tx_effects_internal(tx_response.as_ref()).map(ProposalResult::Executed) } else { // 2 objects are created, one is the Bag's Field and the other is our Proposal. Proposal is not owned by the bag, // but the field is. let proposals_bag_id = identity.multicontroller().proposals_bag_id(); let proposal_id = tx_response - .effects - .as_ref() + .effects_created() .ok_or_else(|| Error::TransactionUnexpectedResponse("transaction had no effects".to_string()))? - .created() .iter() .find(|obj_ref| obj_ref.owner != proposals_bag_id) .expect("tx was successful") @@ -223,7 +257,7 @@ where client.get_object_by_id(proposal_id).await.map(ProposalResult::Pending) } - .map(move |output| TransactionOutput { + .map(move |output| TransactionOutputInternal { output, response: tx_response, }) @@ -233,41 +267,41 @@ where /// A transaction to execute a [`Proposal`]. #[derive(Debug)] pub struct ExecuteProposalTx<'i, A> { - tx: ProgrammableTransaction, + tx: ProgrammableTransactionBcs, identity: &'i mut OnChainIdentity, _action: PhantomData, } -#[async_trait] -impl Transaction for ExecuteProposalTx<'_, A> +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for ExecuteProposalTx<'_, A> where Proposal: ProposalT, - A: Send, + A: OptionalSend, { type Output = as ProposalT>::Output; - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { let Self { identity, tx, .. } = self; let tx_response = client.execute_transaction(tx, gas_budget).await?; - let tx_effects_status = tx_response - .effects - .as_ref() + let tx_effects_execution_status = tx_response + .effects_execution_status() .ok_or_else(|| Error::TransactionUnexpectedResponse("missing effects".to_string()))?; - if let IotaExecutionStatus::Failure { error } = tx_effects_status.status() { + if let IotaExecutionStatus::Failure { error } = tx_effects_execution_status { Err(Error::TransactionUnexpectedResponse(error.clone())) } else { *identity = get_identity(client, identity.id()) .await? .expect("identity exists on-chain"); - Proposal::::parse_tx_effects(&tx_response).map(move |output| TransactionOutput { + Proposal::::parse_tx_effects_internal(tx_response.as_ref()).map(move |output| TransactionOutputInternal { output, response: tx_response, }) @@ -282,25 +316,26 @@ pub struct ApproveProposalTx<'p, 'i, A> { identity: &'i OnChainIdentity, } -#[async_trait] -impl Transaction for ApproveProposalTx<'_, '_, A> +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for ApproveProposalTx<'_, '_, A> where Proposal: ProposalT, A: MoveType + Send, { type Output = (); - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { let Self { proposal, identity, .. } = self; let identity_ref = client.get_object_ref_by_id(identity.id()).await?.unwrap(); let controller_cap = identity.get_controller_cap(client).await?; - let tx = move_calls::identity::proposal::approve::( + let tx = ::approve_proposal::( identity_ref.clone(), controller_cap, proposal.id(), @@ -308,12 +343,11 @@ where )?; let response = client.execute_transaction(tx, gas_budget).await?; - let tx_effects_status = response - .effects - .as_ref() + let tx_effects_execution_status = response + .effects_execution_status() .ok_or_else(|| Error::TransactionUnexpectedResponse("missing effects".to_string()))?; - if let IotaExecutionStatus::Failure { error } = tx_effects_status.status() { + if let IotaExecutionStatus::Failure { error } = tx_effects_execution_status { return Err(Error::TransactionUnexpectedResponse(error.clone())); } @@ -322,7 +356,7 @@ where .expect("is identity's controller"); *proposal.votes_mut() = proposal.votes() + vp; - Ok(TransactionOutput { output: (), response }) + Ok(TransactionOutputInternal { output: (), response }) } } diff --git a/identity_iota_core/src/rebased/proposals/send.rs b/identity_iota_core/src/rebased/proposals/send.rs index 00bffcacf9..cdebc4d9fa 100644 --- a/identity_iota_core/src/rebased/proposals/send.rs +++ b/identity_iota_core/src/rebased/proposals/send.rs @@ -4,20 +4,26 @@ use std::marker::PhantomData; use async_trait::async_trait; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::TypeTag; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; +use crate::iota_interaction_adapter::AdapterError; +use crate::iota_interaction_adapter::AdapterNativeResponse; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::OptionalSync; + use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use crate::rebased::migration::OnChainIdentity; -use crate::rebased::utils::MoveType; use crate::rebased::Error; +use identity_iota_interaction::MoveType; use super::CreateProposalTx; use super::ExecuteProposalTx; @@ -72,10 +78,12 @@ impl ProposalBuilder<'_, SendAction> { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProposalT for Proposal { type Action = SendAction; type Output = (); + type Response = IotaTransactionBlockResponseAdapter; async fn create<'i, S>( action: Self::Action, @@ -84,7 +92,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let identity_ref = client .get_object_ref_by_id(identity.id()) @@ -108,7 +116,7 @@ impl ProposalT for Proposal { } object_and_type_list }; - move_calls::identity::create_and_execute_send( + IdentityMoveCallsAdapter::create_and_execute_send( identity_ref, controller_cap_ref, action.0, @@ -117,7 +125,7 @@ impl ProposalT for Proposal { client.package_id(), ) } else { - move_calls::identity::propose_send( + IdentityMoveCallsAdapter::propose_send( identity_ref, controller_cap_ref, action.0, @@ -140,7 +148,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let proposal_id = self.id(); let identity_ref = client @@ -162,7 +170,7 @@ impl ProposalT for Proposal { object_and_type_list }; - let tx = move_calls::identity::execute_send( + let tx = IdentityMoveCallsAdapter::execute_send( identity_ref, controller_cap_ref, proposal_id, @@ -178,7 +186,9 @@ impl ProposalT for Proposal { }) } - fn parse_tx_effects(_tx_response: &IotaTransactionBlockResponse) -> Result { + fn parse_tx_effects_internal( + _tx_response: &dyn IotaTransactionBlockResponseT, + ) -> Result { Ok(()) } } diff --git a/identity_iota_core/src/rebased/proposals/update_did_doc.rs b/identity_iota_core/src/rebased/proposals/update_did_doc.rs index e3c5253fd0..3e6ed7fd93 100644 --- a/identity_iota_core/src/rebased/proposals/update_did_doc.rs +++ b/identity_iota_core/src/rebased/proposals/update_did_doc.rs @@ -3,22 +3,28 @@ use std::marker::PhantomData; +use crate::iota_interaction_adapter::AdapterError; +use crate::iota_interaction_adapter::AdapterNativeResponse; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::OptionalSync; + use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use crate::IotaDocument; use async_trait::async_trait; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::TypeTag; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; use crate::rebased::migration::OnChainIdentity; use crate::rebased::migration::Proposal; -use crate::rebased::utils::MoveType; use crate::rebased::Error; +use identity_iota_interaction::MoveType; use super::CreateProposalTx; use super::ExecuteProposalTx; @@ -44,10 +50,12 @@ impl UpdateDidDocument { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProposalT for Proposal { type Action = UpdateDidDocument; type Output = (); + type Response = IotaTransactionBlockResponseAdapter; async fn create<'i, S>( action: Self::Action, @@ -56,7 +64,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let identity_ref = client .get_object_ref_by_id(identity.id()) @@ -67,7 +75,7 @@ impl ProposalT for Proposal { .controller_voting_power(controller_cap_ref.0) .expect("controller exists"); let chained_execution = sender_vp >= identity.threshold(); - let tx = move_calls::identity::propose_update( + let tx = IdentityMoveCallsAdapter::propose_update( identity_ref, controller_cap_ref, action.0, @@ -90,7 +98,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let proposal_id = self.id(); let identity_ref = client @@ -99,8 +107,9 @@ impl ProposalT for Proposal { .expect("identity exists on-chain"); let controller_cap_ref = identity.get_controller_cap(client).await?; - let tx = move_calls::identity::execute_update(identity_ref, controller_cap_ref, proposal_id, client.package_id()) - .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; + let tx = + IdentityMoveCallsAdapter::execute_update(identity_ref, controller_cap_ref, proposal_id, client.package_id()) + .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(ExecuteProposalTx { identity, @@ -109,7 +118,9 @@ impl ProposalT for Proposal { }) } - fn parse_tx_effects(_tx_response: &IotaTransactionBlockResponse) -> Result { + fn parse_tx_effects_internal( + _tx_response: &dyn IotaTransactionBlockResponseT, + ) -> Result { Ok(()) } } diff --git a/identity_iota_core/src/rebased/proposals/upgrade.rs b/identity_iota_core/src/rebased/proposals/upgrade.rs index 19914f806c..db796853d1 100644 --- a/identity_iota_core/src/rebased/proposals/upgrade.rs +++ b/identity_iota_core/src/rebased/proposals/upgrade.rs @@ -3,21 +3,27 @@ use std::marker::PhantomData; +use crate::iota_interaction_adapter::AdapterError; +use crate::iota_interaction_adapter::AdapterNativeResponse; +use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdapter; +use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::OptionalSync; + use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; -use crate::rebased::iota::move_calls; use async_trait::async_trait; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::types::base_types::ObjectID; +use identity_iota_interaction::types::TypeTag; use secret_storage::Signer; use serde::Deserialize; use serde::Serialize; use crate::rebased::migration::OnChainIdentity; use crate::rebased::migration::Proposal; -use crate::rebased::utils::MoveType; use crate::rebased::Error; +use identity_iota_interaction::MoveType; use super::CreateProposalTx; use super::ExecuteProposalTx; @@ -42,10 +48,14 @@ impl MoveType for Upgrade { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProposalT for Proposal { type Action = Upgrade; type Output = (); + type Response = IotaTransactionBlockResponseAdapter; async fn create<'i, S>( _action: Self::Action, @@ -54,7 +64,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let identity_ref = client .get_object_ref_by_id(identity.id()) @@ -65,8 +75,9 @@ impl ProposalT for Proposal { .controller_voting_power(controller_cap_ref.0) .expect("controller exists"); let chained_execution = sender_vp >= identity.threshold(); - let tx = move_calls::identity::propose_upgrade(identity_ref, controller_cap_ref, expiration, client.package_id()) - .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; + let tx = + IdentityMoveCallsAdapter::propose_upgrade(identity_ref, controller_cap_ref, expiration, client.package_id()) + .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(CreateProposalTx { identity, @@ -82,7 +93,7 @@ impl ProposalT for Proposal { client: &IdentityClient, ) -> Result, Error> where - S: Signer + Sync, + S: Signer + OptionalSync, { let proposal_id = self.id(); let identity_ref = client @@ -91,8 +102,9 @@ impl ProposalT for Proposal { .expect("identity exists on-chain"); let controller_cap_ref = identity.get_controller_cap(client).await?; - let tx = move_calls::identity::execute_upgrade(identity_ref, controller_cap_ref, proposal_id, client.package_id()) - .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; + let tx = + IdentityMoveCallsAdapter::execute_upgrade(identity_ref, controller_cap_ref, proposal_id, client.package_id()) + .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?; Ok(ExecuteProposalTx { identity, @@ -101,7 +113,9 @@ impl ProposalT for Proposal { }) } - fn parse_tx_effects(_tx_response: &IotaTransactionBlockResponse) -> Result { + fn parse_tx_effects_internal( + _tx_response: &dyn IotaTransactionBlockResponseT, + ) -> Result { Ok(()) } } diff --git a/identity_iota_core/src/rebased/transaction.rs b/identity_iota_core/src/rebased/transaction.rs index 659b45d1e2..73910b0f97 100644 --- a/identity_iota_core/src/rebased/transaction.rs +++ b/identity_iota_core/src/rebased/transaction.rs @@ -3,16 +3,23 @@ use std::ops::Deref; -use async_trait::async_trait; -use iota_sdk::rpc_types::IotaTransactionBlockResponse; -use iota_sdk::types::transaction::ProgrammableTransaction; -use secret_storage::Signer; +#[cfg(not(target_arch = "wasm32"))] +use identity_iota_interaction::rpc_types::IotaTransactionBlockResponse; +#[cfg(not(target_arch = "wasm32"))] +use identity_iota_interaction::types::transaction::ProgrammableTransaction; +#[cfg(target_arch = "wasm32")] +use iota_interaction_ts::ProgrammableTransaction; +use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdaptedTraitObj; use crate::rebased::client::IdentityClient; -use crate::rebased::client::IotaKeySignature; use crate::rebased::Error; +use async_trait::async_trait; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::ProgrammableTransactionBcs; +use secret_storage::Signer; /// The output type of a [`Transaction`]. +#[cfg(not(target_arch = "wasm32"))] #[derive(Debug, Clone)] pub struct TransactionOutput { /// The parsed Transaction output. See [`Transaction::Output`]. @@ -21,6 +28,7 @@ pub struct TransactionOutput { pub response: IotaTransactionBlockResponse, } +#[cfg(not(target_arch = "wasm32"))] impl Deref for TransactionOutput { type Target = T; fn deref(&self) -> &Self::Target { @@ -29,7 +37,8 @@ impl Deref for TransactionOutput { } /// Interface for operations that interact with the ledger through transactions. -#[async_trait] +#[cfg(not(target_arch = "wasm32"))] +#[async_trait::async_trait] pub trait Transaction: Sized { /// The result of performing the operation. type Output; @@ -60,19 +69,123 @@ pub trait Transaction: Sized { } } -#[async_trait] -impl Transaction for ProgrammableTransaction { +pub(crate) struct TransactionOutputInternal { + pub output: T, + pub response: IotaTransactionBlockResponseAdaptedTraitObj, +} + +impl Deref for TransactionOutputInternal { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.output + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl From> for TransactionOutput { + fn from(value: TransactionOutputInternal) -> Self { + let TransactionOutputInternal:: { + output: out, + response: internal_response, + } = value; + let response = internal_response.clone_native_response(); + TransactionOutput { output: out, response } + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub(crate) trait TransactionInternal: Sized { + type Output; + + async fn execute_with_opt_gas_internal + Sync>( + self, + gas_budget: Option, + client: &IdentityClient, + ) -> Result, Error>; + + #[cfg(target_arch = "wasm32")] + async fn execute + Sync>( + self, + client: &IdentityClient, + ) -> Result, Error> { + self.execute_with_opt_gas_internal(None, client).await + } + + #[cfg(target_arch = "wasm32")] + async fn execute_with_gas + Sync>( + self, + gas_budget: u64, + client: &IdentityClient, + ) -> Result, Error> { + self.execute_with_opt_gas_internal(Some(gas_budget), client).await + } +} + +#[cfg(not(target_arch = "wasm32"))] +#[async_trait::async_trait] +impl + Send, O> Transaction for T { + type Output = O; + + async fn execute_with_opt_gas + Sync>( + self, + gas_budget: Option, + client: &IdentityClient, + ) -> Result, Error> { + let tx_output = self.execute_with_opt_gas_internal(gas_budget, client).await?; + Ok(tx_output.into()) + } +} + +#[cfg(not(target_arch = "wasm32"))] +#[async_trait::async_trait] +impl TransactionInternal for ProgrammableTransaction { + type Output = (); + async fn execute_with_opt_gas_internal( + self, + gas_budget: Option, + client: &IdentityClient, + ) -> Result, Error> + where + S: Signer + Sync, + { + let tx_bcs = bcs::to_bytes(&self)?; + let response = client.execute_transaction(tx_bcs, gas_budget).await?; + Ok(TransactionOutputInternal { output: (), response }) + } +} + +#[cfg(target_arch = "wasm32")] +#[async_trait::async_trait(?Send)] +impl TransactionInternal for ProgrammableTransaction { type Output = (); - async fn execute_with_opt_gas( + async fn execute_with_opt_gas_internal( self, gas_budget: Option, client: &IdentityClient, - ) -> Result, Error> + ) -> Result, Error> where S: Signer + Sync, { - let response = client.execute_transaction(self, gas_budget).await?; - Ok(TransactionOutput { output: (), response }) + unimplemented!("TransactionInternal::execute_with_opt_gas_internal for ProgrammableTransaction"); + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TransactionInternal for ProgrammableTransactionBcs { + type Output = (); + + async fn execute_with_opt_gas_internal + Sync>( + self, + gas_budget: Option, + client: &IdentityClient, + ) -> Result, Error> { + // For wasm32 targets, the following line will result in a compiler error[E0412] + // TODO: Implement wasm-bindings for the ProgrammableTransaction TS equivalent + // and use them to do the BCS serialization + let self_tx = bcs::from_bytes::(&self)?; + self_tx.execute_with_opt_gas_internal(gas_budget, client).await } } @@ -95,7 +208,7 @@ pub trait ProtoTransaction { // and that has itself as its next state. impl ProtoTransaction for T where - T: Transaction, + T: TransactionInternal, { type Input = (); type Tx = Self; diff --git a/identity_iota_core/src/rebased/utils.rs b/identity_iota_core/src/rebased/utils.rs index 88e90b4da7..f48eb9144b 100644 --- a/identity_iota_core/src/rebased/utils.rs +++ b/identity_iota_core/src/rebased/utils.rs @@ -4,19 +4,13 @@ use std::process::Output; use anyhow::Context as _; -use iota_sdk::types::base_types::ObjectID; -use iota_sdk::types::programmable_transaction_builder::ProgrammableTransactionBuilder; -use iota_sdk::types::transaction::Argument; -use iota_sdk::types::TypeTag; +use identity_iota_interaction::types::base_types::ObjectID; use serde::Deserialize; -use serde::Serialize; +#[cfg(not(target_arch = "wasm32"))] use tokio::process::Command; -use iota_sdk::types::base_types::IotaAddress; -use iota_sdk::IotaClient; -use iota_sdk::IotaClientBuilder; - use crate::rebased::Error; +use identity_iota_interaction::types::base_types::IotaAddress; const FUND_WITH_ACTIVE_ADDRESS_FUNDING_TX_BUDGET: u64 = 5_000_000; const FUND_WITH_ACTIVE_ADDRESS_FUNDING_VALUE: u64 = 500_000_000; @@ -28,14 +22,20 @@ struct CoinOutput { nanos_balance: u64, } -/// Builds an `IOTA` client for the given network. -pub async fn get_client(network: &str) -> Result { - let client = IotaClientBuilder::default() - .build(network) - .await - .map_err(|err| Error::Network(format!("failed to connect to {network}"), err))?; +cfg_if::cfg_if! { + if #[cfg(not(target_arch = "wasm32"))] { + use iota_sdk::{IotaClientBuilder, IotaClient}; + + /// Builds an `IOTA` client for the given network. + pub async fn get_client(network: &str) -> Result { + let client = IotaClientBuilder::default() + .build(network) + .await + .map_err(|err| Error::Network(format!("failed to connect to {network}"), err))?; - Ok(client) + Ok(client) + } + } } fn unpack_command_output(output: &Output, task: &str) -> anyhow::Result { @@ -54,6 +54,7 @@ fn unpack_command_output(output: &Output, task: &str) -> anyhow::Result /// For that the env variable `IOTA_IDENTITY_FUND_WITH_ACTIVE_ADDRESS` must be set to `true`. /// Notice, that this is a setting mostly intended for internal test use and must be used with care. /// For details refer to to `identity_iota_core`'s README.md. +#[cfg(not(target_arch = "wasm32"))] pub async fn request_funds(address: &IotaAddress) -> anyhow::Result<()> { let fund_with_active_address = std::env::var("IOTA_IDENTITY_FUND_WITH_ACTIVE_ADDRESS") .map(|v| !v.is_empty() && v.to_lowercase() == "true") @@ -108,66 +109,3 @@ pub async fn request_funds(address: &IotaAddress) -> anyhow::Result<()> { Ok(()) } - -/// Trait for types that can be converted to a Move type. -pub trait MoveType: Serialize { - /// Returns the Move type for this type. - fn move_type(package: ObjectID) -> TypeTag; - - /// Tries to convert this type to a Move argument. - fn try_to_argument( - &self, - ptb: &mut ProgrammableTransactionBuilder, - _package: Option, - ) -> Result { - ptb.pure(self).map_err(|e| Error::InvalidArgument(e.to_string())) - } -} - -impl MoveType for u8 { - fn move_type(_package: ObjectID) -> TypeTag { - TypeTag::U8 - } -} - -impl MoveType for u16 { - fn move_type(_package: ObjectID) -> TypeTag { - TypeTag::U16 - } -} - -impl MoveType for u32 { - fn move_type(_package: ObjectID) -> TypeTag { - TypeTag::U32 - } -} - -impl MoveType for u64 { - fn move_type(_package: ObjectID) -> TypeTag { - TypeTag::U64 - } -} - -impl MoveType for u128 { - fn move_type(_package: ObjectID) -> TypeTag { - TypeTag::U128 - } -} - -impl MoveType for bool { - fn move_type(_package: ObjectID) -> TypeTag { - TypeTag::Bool - } -} - -impl MoveType for IotaAddress { - fn move_type(_package: ObjectID) -> TypeTag { - TypeTag::Address - } -} - -impl MoveType for Vec { - fn move_type(package: ObjectID) -> TypeTag { - TypeTag::Vector(Box::new(T::move_type(package))) - } -} diff --git a/identity_iota_core/src/state_metadata/document.rs b/identity_iota_core/src/state_metadata/document.rs index 041aaaada0..5919e8a2d0 100644 --- a/identity_iota_core/src/state_metadata/document.rs +++ b/identity_iota_core/src/state_metadata/document.rs @@ -423,10 +423,7 @@ mod tests { // Encoding. assert_eq!(packed[4], StateMetadataEncoding::Json as u8); // JSON length. - assert_eq!( - &packed[5..=6], - (expected_payload.as_bytes().len() as u16).to_le_bytes().as_ref() - ); + assert_eq!(&packed[5..=6], (expected_payload.len() as u16).to_le_bytes().as_ref()); // JSON payload. assert_eq!(&packed[7..], expected_payload.as_bytes()); } diff --git a/identity_iota_core/tests/e2e/asset.rs b/identity_iota_core/tests/e2e/asset.rs index b0dbd40837..ddab27ba2a 100644 --- a/identity_iota_core/tests/e2e/asset.rs +++ b/identity_iota_core/tests/e2e/asset.rs @@ -15,12 +15,13 @@ use identity_credential::validator::JwtCredentialValidator; use identity_document::document::CoreDocument; use identity_eddsa_verifier::EdDSAJwsVerifier; use identity_iota_core::rebased::transaction::Transaction; -use identity_iota_core::rebased::utils::MoveType as _; use identity_iota_core::rebased::AuthenticatedAsset; use identity_iota_core::rebased::PublicAvailableVC; use identity_iota_core::rebased::TransferProposal; use identity_iota_core::IotaDID; use identity_iota_core::IotaDocument; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::MoveType as _; use identity_storage::JwkDocumentExt; use identity_storage::JwsSignatureOptions; use identity_verification::VerificationMethod; diff --git a/identity_iota_core/tests/e2e/common.rs b/identity_iota_core/tests/e2e/common.rs index d9c67198b7..0edb20502c 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -6,10 +6,10 @@ use anyhow::anyhow; use anyhow::Context; use identity_iota_core::rebased::client::IdentityClient; use identity_iota_core::rebased::client::IdentityClientReadOnly; -use identity_iota_core::rebased::client::IotaKeySignature; use identity_iota_core::rebased::transaction::Transaction; use identity_iota_core::rebased::utils::request_funds; use identity_iota_core::IotaDID; +use identity_iota_interaction::IotaKeySignature; use identity_jose::jwk::Jwk; use identity_jose::jws::JwsAlgorithm; use identity_storage::JwkMemStore; diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml new file mode 100644 index 0000000000..bf83d6f2fa --- /dev/null +++ b/identity_iota_interaction/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "identity_iota_interaction" +version = "1.4.0" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +keywords = ["iota", "tangle", "identity"] +license.workspace = true +readme = "./README.md" +repository.workspace = true +rust-version.workspace = true +description = "Trait definitions and a wasm32 compatible subset of code, copied from the IOTA Rust SDK, used to replace the IOTA Rust SDK for wasm32 builds." + +[dependencies] +anyhow = "1.0.75" +async-trait = { version = "0.1.81", default-features = false } +cfg-if = "1.0.0" +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.1.0" } +serde.workspace = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.7.3-rc" } +move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.7.3-rc" } + +shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.7.3-rc" } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +bcs = "0.1.4" +eyre = { version = "0.6" } +fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto" } +fastcrypto-zkp = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597", package = "fastcrypto-zkp" } +getrandom = { version = "0.2", default-features = false, features = ["js"] } +hex = { version = "0.4" } +itertools = "0.13" +jsonrpsee = { version = "0.24", default-features = false, features = ["wasm-client"] } +leb128 = { version = "0.2" } +num-bigint = { version = "0.4" } +primitive-types = { version = "0.12", features = ["impl-serde"] } +rand = "0.8.5" +ref-cast = { version = "1.0" } +serde_json.workspace = true +serde_repr = { version = "0.1" } +serde_with = { version = "3.8", features = ["hex"] } +strum.workspace = true +thiserror.workspace = true +tracing = { version = "0.1" } +uint = { version = "0.9" } + +[package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps --workspace --open +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[features] +default = ["send-sync-transaction", "secret-storage/default"] +send-sync-transaction = ["secret-storage/send-sync-storage"] + +[lints] +workspace = true diff --git a/identity_iota_interaction/README.md b/identity_iota_interaction/README.md new file mode 100644 index 0000000000..abee8ad9e1 --- /dev/null +++ b/identity_iota_interaction/README.md @@ -0,0 +1,51 @@ +# Platform Agnostic Iota Interaction + +This crate gathers types needed to interact with IOTA nodes in a platform-agnostic way +to allow building the Identity library for WASM32 architectures. + +The folder `sdk_types`, contained in this crate, provides a selection of +code copied from the iotaledger/iota.git repository: + +| Folder Name | Original Source in iotaledger/iota.git | +|------------------------------------|------------------------------------------------------| +| sdk_types/iota_json_rpc_types | crates/iota-json-rpc-types | +| sdk_types/iota_types | crates/iota-types | +| sdk_types/move_command_line_common | external-crates/move/crates/move-command-line-common | +| sdk_types/move_core_types | external-crates/move/crates/move-core-types | +| sdk_types/shared_crypto | crates/shared-crypto/Cargo.toml | + +The folder structure in `sdk_types` matches the way the original IOTA Client Rust SDK +provides the above listed crates via `pub use`. + +This crate (file 'lib.rs' contained in this folder) provides several +`build target` specific `pub use` and `type` expressions: + +* For **NON wasm32 targets**, the original _IOTA Client Rust SDK_ sources are provided +* For **WASM32 targets** the code contained in the `sdk_types` folder is used + +Please make sure always to import the SDK dependencies via `use identity_iota::iota_interaction::...` +instead of `use iota_sdk::...` in your code. This way the dependencies needed for your +code are automatically switched according to the currently used build target. + +The Advantage of this target specific dependency switching is, +that for NON wasm32 targets no type marshalling is needed because +the original Rust SDK types are used. + +The drawback of target specific dependency switching is, that code of +the original Rust SDK could be used, that is not contained in the +`sdk_types` folder. The following todos result from this drawback: + +TODOs: + +* Always build your code additionally for the wasm32-unknown-unknown target + before committing your code:
+ `cargo build --package identity_iota_.... --lib --target wasm32-unknown-unknown` +* We need to add tests for the wasm32-unknown-unknown target in the CI toolchain + to make sure the code is always buildable for wasm32 targets. + +All cross-platform usable types and traits (cross-platform-traits) +are contained in this crate. +Platform specific adapters (implementing the cross-platform-traits) are contained in +the crate [bindings/wasm/iota_interaction_ts](../../bindings/wasm/iota_interaction_ts) +and in the folder +[identity_iota_core/src/iota_interaction_rust](../../identity_iota_core/src/iota_interaction_rust). \ No newline at end of file diff --git a/identity_iota_interaction/src/iota_client_trait.rs b/identity_iota_interaction/src/iota_client_trait.rs new file mode 100644 index 0000000000..69d177e55c --- /dev/null +++ b/identity_iota_interaction/src/iota_client_trait.rs @@ -0,0 +1,267 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::IotaRpcResult; +use crate::rpc_types::CoinPage; +use crate::rpc_types::EventFilter; +use crate::rpc_types::EventPage; +use crate::rpc_types::IotaExecutionStatus; +use crate::rpc_types::IotaObjectData; +use crate::rpc_types::IotaObjectDataOptions; +use crate::rpc_types::IotaObjectResponse; +use crate::rpc_types::IotaObjectResponseQuery; +use crate::rpc_types::IotaPastObjectResponse; +use crate::rpc_types::IotaTransactionBlockResponseOptions; +use crate::rpc_types::ObjectsPage; +use crate::rpc_types::OwnedObjectRef; +use crate::types::base_types::IotaAddress; +use crate::types::base_types::ObjectID; +use crate::types::base_types::SequenceNumber; +use crate::types::digests::TransactionDigest; +use crate::types::dynamic_field::DynamicFieldName; +use crate::types::event::EventID; +use crate::types::quorum_driver_types::ExecuteTransactionRequestType; +use crate::OptionalSend; +use crate::ProgrammableTransactionBcs; +use crate::SignatureBcs; +use crate::TransactionDataBcs; +use async_trait::async_trait; +use secret_storage::SignatureScheme; +use secret_storage::Signer; +use std::boxed::Box; +use std::option::Option; +use std::result::Result; + +#[cfg(not(target_arch = "wasm32"))] +use std::marker::Send; + +pub struct IotaKeySignature { + pub public_key: Vec, + pub signature: Vec, +} + +impl SignatureScheme for IotaKeySignature { + type PublicKey = Vec; + type Signature = Vec; +} + +//******************************************************************** +// TODO: rename the following traits to have a consistent relation +// between platform specific trait specializations +// and the platform agnostic traits specified in this file: +// * QuorumDriverTrait -> QuorumDriverApiT +// * ReadTrait -> ReadApiT +// * CoinReadTrait -> CoinReadApiT +// * EventTrait -> EventApiT +// +// Platform specific trait specializations are defined +// in modules identity_iota_core::iota_interaction_rust and +// iota_interaction_ts with the following names: +// * QuorumDriverApiAdaptedT +// * ReadApiAdaptedT +// * CoinReadApiAdaptedT +// * EventApiAdaptedT +// * IotaClientAdaptedT +//******************************************************************** + +/// Adapter Allowing to query information from an IotaTransactionBlockResponse instance. +/// As IotaTransactionBlockResponse pulls too many dependencies we need to +/// hide it behind a trait. +#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] +#[cfg_attr(feature = "send-sync-transaction", async_trait)] +pub trait IotaTransactionBlockResponseT: OptionalSend { + /// Error type used + type Error; + /// The response type used in the platform specific client sdk + type NativeResponse; + + /// Indicates if IotaTransactionBlockResponse::effects is None + fn effects_is_none(&self) -> bool; + /// Indicates if there are Some(effects) + fn effects_is_some(&self) -> bool; + + /// Returns Debug representation of the IotaTransactionBlockResponse + fn to_string(&self) -> String; + + /// If effects_is_some(), returns a clone of the IotaTransactionBlockEffectsAPI::status() + /// Otherwise, returns None + fn effects_execution_status(&self) -> Option; + + /// If effects_is_some(), returns IotaTransactionBlockEffectsAPI::created() + /// as owned Vec. + /// Otherwise, returns None + fn effects_created(&self) -> Option>; + + /// Returns a reference to the platform specific client sdk response instance wrapped by this adapter + fn as_native_response(&self) -> &Self::NativeResponse; + + /// Returns a mutable reference to the platform specific client sdk response instance wrapped by this adapter + fn as_mut_native_response(&mut self) -> &mut Self::NativeResponse; + + /// Returns a clone of the wrapped platform specific client sdk response + fn clone_native_response(&self) -> Self::NativeResponse; +} + +#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] +#[cfg_attr(feature = "send-sync-transaction", async_trait)] +pub trait QuorumDriverTrait { + /// Error type used + type Error; + /// The response type used in the platform specific client sdk + type NativeResponse; + + async fn execute_transaction_block( + &self, + tx_data_bcs: &TransactionDataBcs, + signatures: &[SignatureBcs], + options: Option, + request_type: Option, + ) -> IotaRpcResult>>; +} + +#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] +#[cfg_attr(feature = "send-sync-transaction", async_trait)] +pub trait ReadTrait { + /// Error type used + type Error; + /// The response type used in the platform specific client sdk + type NativeResponse; + + async fn get_chain_identifier(&self) -> Result; + + async fn get_dynamic_field_object( + &self, + parent_object_id: ObjectID, + name: DynamicFieldName, + ) -> IotaRpcResult; + + async fn get_object_with_options( + &self, + object_id: ObjectID, + options: IotaObjectDataOptions, + ) -> IotaRpcResult; + + async fn get_owned_objects( + &self, + address: IotaAddress, + query: Option, + cursor: Option, + limit: Option, + ) -> IotaRpcResult; + + async fn get_reference_gas_price(&self) -> IotaRpcResult; + + async fn get_transaction_with_options( + &self, + digest: TransactionDigest, + options: IotaTransactionBlockResponseOptions, + ) -> IotaRpcResult>>; + + async fn try_get_parsed_past_object( + &self, + object_id: ObjectID, + version: SequenceNumber, + options: IotaObjectDataOptions, + ) -> IotaRpcResult; +} + +#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] +#[cfg_attr(feature = "send-sync-transaction", async_trait)] +pub trait CoinReadTrait { + type Error; + + async fn get_coins( + &self, + owner: IotaAddress, + coin_type: Option, + cursor: Option, + limit: Option, + ) -> IotaRpcResult; +} + +#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] +#[cfg_attr(feature = "send-sync-transaction", async_trait)] +pub trait EventTrait { + /// Error type used + type Error; + + async fn query_events( + &self, + query: EventFilter, + cursor: Option, + limit: Option, + descending_order: bool, + ) -> IotaRpcResult; +} + +#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] +#[cfg_attr(feature = "send-sync-transaction", async_trait)] +pub trait IotaClientTrait { + /// Error type used + type Error; + /// The response type used in the platform specific client sdk + type NativeResponse; + + #[cfg(not(feature = "send-sync-transaction"))] + fn quorum_driver_api( + &self, + ) -> Box + '_>; + #[cfg(feature = "send-sync-transaction")] + fn quorum_driver_api( + &self, + ) -> Box + Send + '_>; + + #[cfg(not(feature = "send-sync-transaction"))] + fn read_api(&self) -> Box + '_>; + #[cfg(feature = "send-sync-transaction")] + fn read_api(&self) -> Box + Send + '_>; + + #[cfg(not(feature = "send-sync-transaction"))] + fn coin_read_api(&self) -> Box + '_>; + #[cfg(feature = "send-sync-transaction")] + fn coin_read_api(&self) -> Box + Send + '_>; + + #[cfg(not(feature = "send-sync-transaction"))] + fn event_api(&self) -> Box + '_>; + #[cfg(feature = "send-sync-transaction")] + fn event_api(&self) -> Box + Send + '_>; + + #[cfg(not(feature = "send-sync-transaction"))] + async fn execute_transaction>( + &self, + sender_address: IotaAddress, + sender_public_key: &[u8], + tx_bcs: ProgrammableTransactionBcs, + gas_budget: Option, + signer: &S, + ) -> Result< + Box>, + Self::Error, + >; + #[cfg(feature = "send-sync-transaction")] + async fn execute_transaction + Sync>( + &self, + sender_address: IotaAddress, + sender_public_key: &[u8], + tx_bcs: ProgrammableTransactionBcs, + gas_budget: Option, + signer: &S, + ) -> Result< + Box>, + Self::Error, + >; + + async fn default_gas_budget( + &self, + sender_address: IotaAddress, + tx_bcs: &ProgrammableTransactionBcs, + ) -> Result; + + async fn get_previous_version(&self, iod: IotaObjectData) -> Result, Self::Error>; + + async fn get_past_object( + &self, + object_id: ObjectID, + version: SequenceNumber, + ) -> Result; +} diff --git a/identity_iota_interaction/src/iota_verifiable_credential.rs b/identity_iota_interaction/src/iota_verifiable_credential.rs new file mode 100644 index 0000000000..f28f178a6c --- /dev/null +++ b/identity_iota_interaction/src/iota_verifiable_credential.rs @@ -0,0 +1,39 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::move_types::language_storage::TypeTag; +use crate::types::base_types::ObjectID; +use crate::MoveType; +use crate::TypedValue; +use serde::Deserialize; +use serde::Serialize; +use std::str::FromStr; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IotaVerifiableCredential { + data: Vec, +} + +impl IotaVerifiableCredential { + pub fn new(data: Vec) -> IotaVerifiableCredential { + IotaVerifiableCredential { data } + } + + pub fn data(&self) -> &Vec { + &self.data + } +} + +impl MoveType for IotaVerifiableCredential { + fn move_type(package: ObjectID) -> TypeTag { + TypeTag::from_str(&format!("{package}::public_vc::PublicVc")).expect("valid utf8") + } + + fn get_typed_value(&self, _package: ObjectID) -> TypedValue + where + Self: MoveType, + Self: Sized, + { + TypedValue::IotaVerifiableCredential(self) + } +} diff --git a/identity_iota_interaction/src/lib.rs b/identity_iota_interaction/src/lib.rs new file mode 100644 index 0000000000..ef235d1aa9 --- /dev/null +++ b/identity_iota_interaction/src/lib.rs @@ -0,0 +1,126 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#![allow(missing_docs)] + +mod iota_client_trait; +mod iota_verifiable_credential; +mod move_call_traits; +mod move_type; +mod transaction_builder_trait; + +pub use iota_client_trait::*; +pub use iota_verifiable_credential::*; +pub use move_call_traits::*; +pub use move_type::*; +pub use transaction_builder_trait::*; + +#[cfg(target_arch = "wasm32")] +mod sdk_types; +#[cfg(target_arch = "wasm32")] +pub use sdk_types::*; + +#[cfg(not(target_arch = "wasm32"))] +pub use iota_sdk::*; +#[cfg(not(target_arch = "wasm32"))] +pub use move_core_types as move_types; +#[cfg(not(target_arch = "wasm32"))] +pub use shared_crypto; + +/// BCS serialized Transaction, where a Transaction includes the TransactionData and a Vec +pub type TransactionBcs = Vec; +/// BCS serialized TransactionData +/// TransactionData usually contain the ProgrammableTransaction, sender, kind = ProgrammableTransaction, +/// gas_coin, gas_budget, gas_price, expiration, ... +/// Example usage: +/// * TS: ExecuteTransactionBlockParams::transactionBlock - Base64 encoded TransactionDataBcs +pub type TransactionDataBcs = Vec; +/// BCS serialized Signature +pub type SignatureBcs = Vec; +/// BCS serialized ProgrammableTransaction +/// A ProgrammableTransaction +/// * has `inputs` (or *CallArgs*) and `commands` (or *Transactions*) +/// * is the result of ProgrammableTransactionBuilder::finish() +pub type ProgrammableTransactionBcs = Vec; +/// BCS serialized IotaTransactionBlockResponse +pub type IotaTransactionBlockResponseBcs = Vec; + +// dummy types, have to be replaced with actual types later on +pub type DummySigner = str; +pub type Hashable = Vec; +pub type Identity = (); + +/// `ident_str!` is a compile-time validated macro that constructs a +/// `&'static IdentStr` from a const `&'static str`. +/// +/// ### Example +/// +/// Creating a valid static or const [`IdentStr`]: +/// +/// ```rust +/// use move_core_types::ident_str; +/// use move_core_types::identifier::IdentStr; +/// const VALID_IDENT: &'static IdentStr = ident_str!("MyCoolIdentifier"); +/// +/// const THING_NAME: &'static str = "thing_name"; +/// const THING_IDENT: &'static IdentStr = ident_str!(THING_NAME); +/// ``` +/// +/// In contrast, creating an invalid [`IdentStr`] will fail at compile time: +/// +/// ```rust,compile_fail +/// use move_core_types::{ident_str, identifier::IdentStr}; +/// const INVALID_IDENT: &'static IdentStr = ident_str!("123Foo"); // Fails to compile! +/// ``` +// TODO(philiphayes): this should really be an associated const fn like `IdentStr::new`; +// unfortunately, both unsafe-reborrow and unsafe-transmute don't currently work +// inside const fn's. Only unsafe-transmute works inside static const-blocks +// (but not const-fn's). +#[macro_export] +macro_rules! ident_str { + ($ident:expr) => {{ + // Only static strings allowed. + let s: &'static str = $ident; + + // Only valid identifier strings are allowed. + // Note: Work-around hack to print an error message in a const block. + let is_valid = $crate::move_types::identifier::is_valid(s); + ["String is not a valid Move identifier"][!is_valid as usize]; + + // SAFETY: the following transmute is safe because + // (1) it's equivalent to the unsafe-reborrow inside IdentStr::ref_cast() + // (which we can't use b/c it's not const). + // (2) we've just asserted that IdentStr impls RefCast, which + // already guarantees the transmute is safe (RefCast checks that + // IdentStr(str) is #[repr(transparent)]). + // (3) both in and out lifetimes are 'static, so we're not widening the + // lifetime. (4) we've just asserted that the IdentStr passes the + // is_valid check. + // + // Note: this lint is unjustified and no longer checked. See issue: + // https://github.com/rust-lang/rust-clippy/issues/6372 + #[allow(clippy::transmute_ptr_to_ptr)] + unsafe { + ::std::mem::transmute::<&'static str, &'static $crate::move_types::identifier::IdentStr>(s) + } + }}; +} + +// Alias name for the Send trait controlled by the "send-sync-transaction" feature +cfg_if::cfg_if! { + if #[cfg(feature = "send-sync-transaction")] { + pub trait OptionalSend: Send {} + impl OptionalSend for T where T: Send {} + + pub trait OptionalSync: Sync {} + impl OptionalSync for T where T: Sync {} + } else { + pub trait OptionalSend: {} + impl OptionalSend for T {} + + pub trait OptionalSync: {} + impl OptionalSync for T where T: {} + } +} diff --git a/identity_iota_interaction/src/move_call_traits.rs b/identity_iota_interaction/src/move_call_traits.rs new file mode 100644 index 0000000000..8eb839ece0 --- /dev/null +++ b/identity_iota_interaction/src/move_call_traits.rs @@ -0,0 +1,258 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::collections::HashSet; +use std::iter::IntoIterator; + +use serde::Serialize; + +use crate::rpc_types::IotaObjectData; +use crate::rpc_types::OwnedObjectRef; +use crate::types::base_types::IotaAddress; +use crate::types::base_types::ObjectID; +use crate::types::base_types::ObjectRef; +use crate::types::base_types::SequenceNumber; +use crate::types::transaction::Argument; +use crate::types::TypeTag; +use crate::MoveType; +use crate::ProgrammableTransactionBcs; + +pub trait AssetMoveCalls { + type Error; + + fn new_asset( + inner: T, + mutable: bool, + transferable: bool, + deletable: bool, + package: ObjectID, + ) -> Result; + + fn delete(asset: ObjectRef, package: ObjectID) -> Result; + + fn transfer( + asset: ObjectRef, + recipient: IotaAddress, + package: ObjectID, + ) -> Result; + + fn make_tx( + proposal: (ObjectID, SequenceNumber), + cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + function_name: &'static str, + ) -> Result; + + fn accept_proposal( + proposal: (ObjectID, SequenceNumber), + recipient_cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + ) -> Result; + + fn conclude_or_cancel( + proposal: (ObjectID, SequenceNumber), + sender_cap: ObjectRef, + asset: ObjectRef, + asset_type_param: TypeTag, + package: ObjectID, + ) -> Result; + + fn update( + asset: ObjectRef, + new_content: T, + package: ObjectID, + ) -> Result; +} + +pub trait MigrationMoveCalls { + type Error; + + fn migrate_did_output( + did_output: ObjectRef, + creation_timestamp: Option, + migration_registry: OwnedObjectRef, + package: ObjectID, + ) -> anyhow::Result; +} + +pub trait BorrowIntentFnInternalT: FnOnce(&mut B, &HashMap) {} +impl BorrowIntentFnInternalT for T where T: FnOnce(&mut B, &HashMap) {} + +pub trait ControllerIntentFnInternalT: FnOnce(&mut B, &Argument) {} +impl ControllerIntentFnInternalT for T where T: FnOnce(&mut B, &Argument) {} + +pub trait IdentityMoveCalls { + type Error; + type NativeTxBuilder; + + fn propose_borrow( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + expiration: Option, + package_id: ObjectID, + ) -> Result; + + fn execute_borrow>( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + objects: Vec, + intent_fn: F, + package: ObjectID, + ) -> Result; + + fn create_and_execute_borrow( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + intent_fn: F, + expiration: Option, + package_id: ObjectID, + ) -> anyhow::Result + where + F: BorrowIntentFnInternalT; + + // We allow clippy::too_many_arguments here because splitting this trait function into multiple + // other functions or creating an options struct gathering multiple function arguments has lower + // priority at the moment. + // TODO: remove clippy::too_many_arguments allowance here + #[allow(clippy::too_many_arguments)] + fn propose_config_change( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + expiration: Option, + threshold: Option, + controllers_to_add: I1, + controllers_to_remove: HashSet, + controllers_to_update: I2, + package: ObjectID, + ) -> Result + where + I1: IntoIterator, + I2: IntoIterator; + + fn execute_config_change( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + proposal_id: ObjectID, + package: ObjectID, + ) -> Result; + + fn propose_controller_execution( + identity: OwnedObjectRef, + capability: ObjectRef, + controller_cap_id: ObjectID, + expiration: Option, + package_id: ObjectID, + ) -> Result; + + fn execute_controller_execution>( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + borrowing_controller_cap_ref: ObjectRef, + intent_fn: F, + package: ObjectID, + ) -> Result; + + fn create_and_execute_controller_execution( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + borrowing_controller_cap_ref: ObjectRef, + intent_fn: F, + package_id: ObjectID, + ) -> Result + where + F: ControllerIntentFnInternalT; + + fn new_identity(did_doc: &[u8], package_id: ObjectID) -> Result; + + fn new_with_controllers>( + did_doc: &[u8], + controllers: C, + threshold: u64, + package_id: ObjectID, + ) -> Result; + + fn propose_deactivation( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + package_id: ObjectID, + ) -> Result; + + fn execute_deactivation( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result; + + fn approve_proposal( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + proposal_id: ObjectID, + package: ObjectID, + ) -> Result; + + fn propose_send( + identity: OwnedObjectRef, + capability: ObjectRef, + transfer_map: Vec<(ObjectID, IotaAddress)>, + expiration: Option, + package_id: ObjectID, + ) -> Result; + + fn execute_send( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + objects: Vec<(ObjectRef, TypeTag)>, + package: ObjectID, + ) -> Result; + + fn propose_update( + identity: OwnedObjectRef, + capability: ObjectRef, + did_doc: impl AsRef<[u8]>, + expiration: Option, + package_id: ObjectID, + ) -> Result; + + fn execute_update( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result; + + fn create_and_execute_send( + identity: OwnedObjectRef, + capability: ObjectRef, + transfer_map: Vec<(ObjectID, IotaAddress)>, + expiration: Option, + objects: Vec<(ObjectRef, TypeTag)>, + package: ObjectID, + ) -> anyhow::Result; + + fn propose_upgrade( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + package_id: ObjectID, + ) -> Result; + + fn execute_upgrade( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result; +} diff --git a/identity_iota_interaction/src/move_type.rs b/identity_iota_interaction/src/move_type.rs new file mode 100644 index 0000000000..ac49e6ee40 --- /dev/null +++ b/identity_iota_interaction/src/move_type.rs @@ -0,0 +1,75 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::types::base_types::IotaAddress; +use crate::types::base_types::ObjectID; +use crate::types::TypeTag; +use crate::IotaVerifiableCredential; +use serde::Serialize; + +pub enum TypedValue<'a, T: MoveType> { + IotaVerifiableCredential(&'a IotaVerifiableCredential), + Other(&'a T), +} + +/// Trait for types that can be converted to a Move type. +pub trait MoveType: Serialize { + /// Returns the Move type for this type. + fn move_type(package: ObjectID) -> TypeTag; + + fn get_typed_value(&self, _package: ObjectID) -> TypedValue + where + Self: MoveType, + Self: Sized, + { + TypedValue::Other(self) + } +} + +impl MoveType for u8 { + fn move_type(_package: ObjectID) -> TypeTag { + TypeTag::U8 + } +} + +impl MoveType for u16 { + fn move_type(_package: ObjectID) -> TypeTag { + TypeTag::U16 + } +} + +impl MoveType for u32 { + fn move_type(_package: ObjectID) -> TypeTag { + TypeTag::U32 + } +} + +impl MoveType for u64 { + fn move_type(_package: ObjectID) -> TypeTag { + TypeTag::U64 + } +} + +impl MoveType for u128 { + fn move_type(_package: ObjectID) -> TypeTag { + TypeTag::U128 + } +} + +impl MoveType for bool { + fn move_type(_package: ObjectID) -> TypeTag { + TypeTag::Bool + } +} + +impl MoveType for IotaAddress { + fn move_type(_package: ObjectID) -> TypeTag { + TypeTag::Address + } +} + +impl MoveType for Vec { + fn move_type(package: ObjectID) -> TypeTag { + TypeTag::Vector(Box::new(T::move_type(package))) + } +} diff --git a/identity_iota_interaction/src/sdk_types/error.rs b/identity_iota_interaction/src/sdk_types/error.rs new file mode 100644 index 0000000000..444c80a740 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/error.rs @@ -0,0 +1,37 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use super::iota_types::base_types::{IotaAddress, TransactionDigest}; +use thiserror::Error; + +//pub use crate::json_rpc_error::Error as JsonRpcError; + +pub type IotaRpcResult = Result; + +#[derive(Error, Debug)] +pub enum Error { + #[error(transparent)] + Rpc(#[from] jsonrpsee::core::ClientError), + #[error(transparent)] + BcsSerialization(#[from] bcs::Error), + #[error("Subscription error: {0}")] + Subscription(String), + #[error("Failed to confirm tx status for {0:?} within {1} seconds.")] + FailToConfirmTransactionStatus(TransactionDigest, u64), + #[error("Data error: {0}")] + Data(String), + #[error( + "Client/Server api version mismatch, client api version: {client_version}, server api version: {server_version}" + )] + ServerVersionMismatch { + client_version: String, + server_version: String, + }, + #[error("Insufficient fund for address [{address}], requested amount: {amount}")] + InsufficientFund { address: IotaAddress, amount: u128 }, + #[error(transparent)] + Json(#[from] serde_json::Error), + #[error("Error caused by a foreign function interface call: {0}")] + FfiError(String), +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/generated_types.rs b/identity_iota_interaction/src/sdk_types/generated_types.rs new file mode 100644 index 0000000000..1a0ab84f66 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/generated_types.rs @@ -0,0 +1,230 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use fastcrypto::encoding::Base64; +use serde::Deserialize; +use serde::Serialize; + +use super::iota_json_rpc_types::iota_transaction::IotaTransactionBlockResponseOptions; +use super::iota_types::quorum_driver_types::ExecuteTransactionRequestType; + +use crate::rpc_types::EventFilter; +use crate::rpc_types::IotaObjectDataFilter; +use crate::rpc_types::IotaObjectDataOptions; +use crate::types::dynamic_field::DynamicFieldName; +use crate::types::event::EventID; +use crate::types::iota_serde::SequenceNumber; +use crate::SignatureBcs; +use crate::TransactionDataBcs; + +// The types defined in this file: +// * do not exist in the iota rust sdk +// * have an equivalent type in the iota typescript sdk +// * are needed for wasm-bindings +// * have been generated by @iota/sdk/typescript/scripts/generate.ts +// +// As there is no equivalent rust type in the iota rust sdk, we need to +// define equivalent rust types here. + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExecuteTransactionBlockParams { + /// BCS serialized transaction data bytes without its type tag, as base-64 encoded string. + transaction_block: Base64, + /// A list of signatures (`flag || signature || pubkey` bytes, as base-64 encoded string). Signature is committed to + /// the intent message of the transaction data, as base-64 encoded string. + signature: Vec, + /// options for specifying the content to be returned + options: Option, + /// The request type, derived from `IotaTransactionBlockResponseOptions` if None + request_type: Option, +} + +impl ExecuteTransactionBlockParams { + pub fn new( + tx_bytes: &TransactionDataBcs, + signatures: &[SignatureBcs], + options: Option, + request_type: Option, + ) -> Self { + ExecuteTransactionBlockParams { + transaction_block: Base64::from_bytes(&tx_bytes), + signature: signatures.into_iter().map(|sig| Base64::from_bytes(&sig)).collect(), + options, + request_type, + } + } +} + +/// Return the dynamic field object information for a specified object +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetDynamicFieldObjectParams { + /// The ID of the queried parent object + parent_id: String, + /// The Name of the dynamic field + name: DynamicFieldName, +} + +impl GetDynamicFieldObjectParams { + pub fn new(parent_id: String, name: DynamicFieldName) -> Self { + GetDynamicFieldObjectParams { parent_id, name } + } +} + +/// Return the object information for a specified object +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetObjectParams { + /// the ID of the queried object + id: String, + /// options for specifying the content to be returned + options: Option, +} + +impl GetObjectParams { + pub fn new(id: String, options: Option) -> Self { + GetObjectParams { id, options } + } +} + +/// Return the list of objects owned by an address. Note that if the address owns more than +/// `QUERY_MAX_RESULT_LIMIT` objects, the pagination is not accurate, because previous page may have +/// been updated when the next page is fetched. Please use iotax_queryObjects if this is a concern. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetOwnedObjectsParams { + /// the owner's Iota address + owner: String, + /// An optional paging cursor. If provided, the query will start from the next item after the specified + /// cursor. Default to start from the first item if not specified. + cursor: Option, + /// Max number of items returned per page, default to [QUERY_MAX_RESULT_LIMIT] if not specified. + limit: Option, + /// If None, no filter will be applied + filter: Option, + /// config which fields to include in the response, by default only digest is included + options: Option, +} + +impl GetOwnedObjectsParams { + pub fn new( + owner: String, + cursor: Option, + limit: Option, + filter: Option, + options: Option, + ) -> Self { + GetOwnedObjectsParams { + owner, + cursor, + limit, + filter, + options, + } + } +} + +/// Return the transaction response object. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetTransactionBlockParams { + /// the digest of the queried transaction + digest: String, + /// options for specifying the content to be returned + options: Option, +} + +impl GetTransactionBlockParams { + pub fn new(digest: String, options: Option) -> Self { + GetTransactionBlockParams { digest, options } + } +} + +/// Note there is no software-level guarantee/SLA that objects with past versions can be retrieved by +/// this API, even if the object and version exists/existed. The result may vary across nodes depending +/// on their pruning policies. Return the object information for a specified version +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TryGetPastObjectParams { + /// the ID of the queried object + id: String, + /// the version of the queried object. If None, default to the latest known version + version: SequenceNumber, + //// options for specifying the content to be returned + options: Option, +} + +impl TryGetPastObjectParams { + pub fn new(id: String, version: SequenceNumber, options: Option) -> Self { + TryGetPastObjectParams { id, version, options } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum SortOrder { + Ascending, + Descending, +} + +impl SortOrder { + pub fn new(descending_order: bool) -> Self { + return if descending_order { + SortOrder::Descending + } else { + SortOrder::Ascending + }; + } +} + +/// Return list of events for a specified query criteria. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct QueryEventsParams { + /// The event query criteria. See [Event filter](https://docs.iota.io/build/event_api#event-filters) + /// documentation for examples. + query: EventFilter, + /// optional paging cursor + cursor: Option, + /// maximum number of items per page, default to [QUERY_MAX_RESULT_LIMIT] if not specified. + limit: Option, + /// query result ordering, default to false (ascending order), oldest record first. + order: Option, +} + +impl QueryEventsParams { + pub fn new(query: EventFilter, cursor: Option, limit: Option, order: Option) -> Self { + QueryEventsParams { + query, + cursor, + limit, + order, + } + } +} + +/// Return all Coin<`coin_type`> objects owned by an address. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetCoinsParams { + /// the owner's Iota address + owner: String, + /// optional type name for the coin (e.g., 0x168da5bf1f48dafc111b0a488fa454aca95e0b5e::usdc::USDC), + /// default to 0x2::iota::IOTA if not specified. + coin_type: Option, + /// optional paging cursor + cursor: Option, + /// maximum number of items per page + limit: Option, +} + +impl GetCoinsParams { + pub fn new(owner: String, coin_type: Option, cursor: Option, limit: Option) -> Self { + GetCoinsParams { + owner, + coin_type, + cursor, + limit, + } + } +} diff --git a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_coin.rs b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_coin.rs new file mode 100644 index 0000000000..818dbb4fd7 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_coin.rs @@ -0,0 +1,36 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as}; + +use super::super::iota_types::{ + base_types::{ObjectID, ObjectRef, TransactionDigest, SequenceNumber}, + digests::ObjectDigest, + iota_serde::{BigInt, SequenceNumber as AsSequenceNumber} +}; + +use super::Page; + +pub type CoinPage = Page; + +#[serde_as] +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Coin { + pub coin_type: String, + pub coin_object_id: ObjectID, + #[serde_as(as = "AsSequenceNumber")] + pub version: SequenceNumber, + pub digest: ObjectDigest, + #[serde_as(as = "BigInt")] + pub balance: u64, + pub previous_transaction: TransactionDigest, +} + +impl Coin { + pub fn object_ref(&self) -> ObjectRef { + (self.coin_object_id, self.version, self.digest) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs new file mode 100644 index 0000000000..4dff64877a --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs @@ -0,0 +1,120 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; +use serde_json::Value; + +use fastcrypto::encoding::Base58; + +use super::super::iota_types::{ + base_types::{ObjectID, IotaAddress, TransactionDigest}, + event::EventID, + iota_serde::{BigInt, IotaStructTag} +}; +use super::super::move_core_types::{ + identifier::Identifier, + language_storage::{StructTag}, +}; + +use super::{Page}; + +pub type EventPage = Page; + +#[serde_as] +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +#[serde(rename = "Event", rename_all = "camelCase")] +pub struct IotaEvent { + /// Sequential event ID, ie (transaction seq number, event seq number). + /// 1) Serves as a unique event ID for each fullnode + /// 2) Also serves to sequence events for the purposes of pagination and + /// querying. A higher id is an event seen later by that fullnode. + /// This ID is the "cursor" for event querying. + pub id: EventID, + /// Move package where this event was emitted. + pub package_id: ObjectID, + #[serde_as(as = "DisplayFromStr")] + /// Move module where this event was emitted. + pub transaction_module: Identifier, + /// Sender's Iota address. + pub sender: IotaAddress, + #[serde_as(as = "IotaStructTag")] + /// Move event type. + pub type_: StructTag, + /// Parsed json value of the event + pub parsed_json: Value, + #[serde_as(as = "Base58")] + /// Base 58 encoded bcs bytes of the move event + pub bcs: Vec, + /// UTC timestamp in milliseconds since epoch (1/1/1970) + #[serde(skip_serializing_if = "Option::is_none")] + #[serde_as(as = "Option>")] + pub timestamp_ms: Option, +} + +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum EventFilter { + /// Query by sender address. + Sender(IotaAddress), + /// Return events emitted by the given transaction. + Transaction( + /// digest of the transaction, as base-64 encoded string + TransactionDigest, + ), + /// Return events emitted in a specified Package. + Package(ObjectID), + /// Return events emitted in a specified Move module. + /// If the event is defined in Module A but emitted in a tx with Module B, + /// query `MoveModule` by module B returns the event. + /// Query `MoveEventModule` by module A returns the event too. + MoveModule { + /// the Move package ID + package: ObjectID, + /// the module name + #[serde_as(as = "DisplayFromStr")] + module: Identifier, + }, + /// Return events with the given Move event struct name (struct tag). + /// For example, if the event is defined in `0xabcd::MyModule`, and named + /// `Foo`, then the struct tag is `0xabcd::MyModule::Foo`. + MoveEventType( + #[serde_as(as = "IotaStructTag")] + StructTag, + ), + /// Return events with the given Move module name where the event struct is + /// defined. If the event is defined in Module A but emitted in a tx + /// with Module B, query `MoveEventModule` by module A returns the + /// event. Query `MoveModule` by module B returns the event too. + MoveEventModule { + /// the Move package ID + package: ObjectID, + /// the module name + #[serde_as(as = "DisplayFromStr")] + module: Identifier, + }, + MoveEventField { + path: String, + value: Value, + }, + /// Return events emitted in [start_time, end_time] interval + #[serde(rename_all = "camelCase")] + TimeRange { + /// left endpoint of time interval, milliseconds since epoch, inclusive + #[serde_as(as = "BigInt")] + start_time: u64, + /// right endpoint of time interval, milliseconds since epoch, exclusive + #[serde_as(as = "BigInt")] + end_time: u64, + }, + + All(Vec), + Any(Vec), + And(Box, Box), + Or(Box, Box), +} + +pub trait Filter { + fn matches(&self, item: &T) -> bool; +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs new file mode 100644 index 0000000000..787f263fd0 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs @@ -0,0 +1,278 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::BTreeMap; +use std::boxed::Box; +use std::fmt::{self, Display, Formatter, Write}; + +use itertools::Itertools; + +use serde::Deserialize; +use serde::Serialize; +use serde_with::{serde_as}; +use serde_json::{json, Value}; + +use tracing::warn; + +use crate::types::{ + base_types::{IotaAddress, ObjectID}, + iota_serde::IotaStructTag, +}; + +use super::super::move_core_types::{ + language_storage::StructTag, + annotated_value::{MoveStruct, MoveValue}, + identifier::Identifier, +}; + +#[serde_as] +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(untagged, rename = "MoveValue")] +pub enum IotaMoveValue { + // u64 and u128 are converted to String to avoid overflow + Number(u32), + Bool(bool), + Address(IotaAddress), + Vector(Vec), + String(String), + UID { id: ObjectID }, + Struct(IotaMoveStruct), + Option(Box>), +} + +impl IotaMoveValue { + /// Extract values from MoveValue without type information in json format + pub fn to_json_value(self) -> Value { + match self { + IotaMoveValue::Struct(move_struct) => move_struct.to_json_value(), + IotaMoveValue::Vector(values) => IotaMoveStruct::Runtime(values).to_json_value(), + IotaMoveValue::Number(v) => json!(v), + IotaMoveValue::Bool(v) => json!(v), + IotaMoveValue::Address(v) => json!(v), + IotaMoveValue::String(v) => json!(v), + IotaMoveValue::UID { id } => json!({ "id": id }), + IotaMoveValue::Option(v) => json!(v), + } + } +} + +impl Display for IotaMoveValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut writer = String::new(); + match self { + IotaMoveValue::Number(value) => write!(writer, "{value}")?, + IotaMoveValue::Bool(value) => write!(writer, "{value}")?, + IotaMoveValue::Address(value) => write!(writer, "{value}")?, + IotaMoveValue::String(value) => write!(writer, "{value}")?, + IotaMoveValue::UID { id } => write!(writer, "{id}")?, + IotaMoveValue::Struct(value) => write!(writer, "{value}")?, + IotaMoveValue::Option(value) => write!(writer, "{value:?}")?, + IotaMoveValue::Vector(vec) => { + write!( + writer, + "{}", + vec.iter().map(|value| format!("{value}")).join(",\n") + )?; + } + } + write!(f, "{}", writer.trim_end_matches('\n')) + } +} + +impl From for IotaMoveValue { + fn from(value: MoveValue) -> Self { + match value { + MoveValue::U8(value) => IotaMoveValue::Number(value.into()), + MoveValue::U16(value) => IotaMoveValue::Number(value.into()), + MoveValue::U32(value) => IotaMoveValue::Number(value), + MoveValue::U64(value) => IotaMoveValue::String(format!("{value}")), + MoveValue::U128(value) => IotaMoveValue::String(format!("{value}")), + MoveValue::U256(value) => IotaMoveValue::String(format!("{value}")), + MoveValue::Bool(value) => IotaMoveValue::Bool(value), + MoveValue::Vector(values) => { + IotaMoveValue::Vector(values.into_iter().map(|value| value.into()).collect()) + } + MoveValue::Struct(value) => { + // Best effort Iota core type conversion + let MoveStruct { type_, fields } = &value; + if let Some(value) = try_convert_type(type_, fields) { + return value; + } + IotaMoveValue::Struct(value.into()) + } + MoveValue::Signer(value) | MoveValue::Address(value) => { + IotaMoveValue::Address(IotaAddress::from(ObjectID::from(value))) + } + } + } +} + +fn to_bytearray(value: &[MoveValue]) -> Option> { + if value.iter().all(|value| matches!(value, MoveValue::U8(_))) { + let bytearray = value + .iter() + .flat_map(|value| { + if let MoveValue::U8(u8) = value { + Some(*u8) + } else { + None + } + }) + .collect::>(); + Some(bytearray) + } else { + None + } +} + +#[serde_as] +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(untagged, rename = "MoveStruct")] +pub enum IotaMoveStruct { + Runtime(Vec), + WithTypes { + #[serde(rename = "type")] + #[serde_as(as = "IotaStructTag")] + type_: StructTag, + fields: BTreeMap, + }, + WithFields(BTreeMap), +} + +impl IotaMoveStruct { + /// Extract values from MoveStruct without type information in json format + pub fn to_json_value(self) -> Value { + // Unwrap MoveStructs + match self { + IotaMoveStruct::Runtime(values) => { + let values = values + .into_iter() + .map(|value| value.to_json_value()) + .collect::>(); + json!(values) + } + // We only care about values here, assuming struct type information is known at the + // client side. + IotaMoveStruct::WithTypes { type_: _, fields } | IotaMoveStruct::WithFields(fields) => { + let fields = fields + .into_iter() + .map(|(key, value)| (key, value.to_json_value())) + .collect::>(); + json!(fields) + } + } + } + + pub fn read_dynamic_field_value(&self, field_name: &str) -> Option { + match self { + IotaMoveStruct::WithFields(fields) => fields.get(field_name).cloned(), + IotaMoveStruct::WithTypes { type_: _, fields } => fields.get(field_name).cloned(), + _ => None, + } + } +} + +impl Display for IotaMoveStruct { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut writer = String::new(); + match self { + IotaMoveStruct::Runtime(_) => {} + IotaMoveStruct::WithFields(fields) => { + for (name, value) in fields { + writeln!(writer, "{}: {value}", name)?; + } + } + IotaMoveStruct::WithTypes { type_, fields } => { + writeln!(writer)?; + writeln!(writer, " {}: {type_}", "type")?; + for (name, value) in fields { + let value = format!("{value}"); + let value = if value.starts_with('\n') { + indent(&value, 2) + } else { + value + }; + writeln!(writer, " {}: {value}", name)?; + } + } + } + write!(f, "{}", writer.trim_end_matches('\n')) + } +} + +fn indent(d: &T, indent: usize) -> String { + d.to_string() + .lines() + .map(|line| format!("{:indent$}{line}", "")) + .join("\n") +} + +fn try_convert_type( + type_: &StructTag, + fields: &[(Identifier, MoveValue)], +) -> Option { + let struct_name = format!( + "0x{}::{}::{}", + type_.address.short_str_lossless(), + type_.module, + type_.name + ); + let mut values = fields + .iter() + .map(|(id, value)| (id.to_string(), value)) + .collect::>(); + match struct_name.as_str() { + "0x1::string::String" | "0x1::ascii::String" => { + if let Some(MoveValue::Vector(bytes)) = values.remove("bytes") { + return to_bytearray(bytes) + .and_then(|bytes| String::from_utf8(bytes).ok()) + .map(IotaMoveValue::String); + } + } + "0x2::url::Url" => { + return values.remove("url").cloned().map(IotaMoveValue::from); + } + "0x2::object::ID" => { + return values.remove("bytes").cloned().map(IotaMoveValue::from); + } + "0x2::object::UID" => { + let id = values.remove("id").cloned().map(IotaMoveValue::from); + if let Some(IotaMoveValue::Address(address)) = id { + return Some(IotaMoveValue::UID { + id: ObjectID::from(address), + }); + } + } + "0x2::balance::Balance" => { + return values.remove("value").cloned().map(IotaMoveValue::from); + } + "0x1::option::Option" => { + if let Some(MoveValue::Vector(values)) = values.remove("vec") { + return Some(IotaMoveValue::Option(Box::new( + // in Move option is modeled as vec of 1 element + values.first().cloned().map(IotaMoveValue::from), + ))); + } + } + _ => return None, + } + warn!( + fields =? fields, + "Failed to convert {struct_name} to IotaMoveValue" + ); + None +} + +impl From for IotaMoveStruct { + fn from(move_struct: MoveStruct) -> Self { + IotaMoveStruct::WithTypes { + type_: move_struct.type_, + fields: move_struct + .fields + .into_iter() + .map(|(id, value)| (id.into_string(), value.into())) + .collect(), + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs new file mode 100644 index 0000000000..1774febe5a --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs @@ -0,0 +1,797 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::BTreeMap; +use std::string::String; +use std::fmt::{self, Display, Formatter, Write}; +use std::cmp::Ordering; + +use serde::Deserialize; +use serde::Serialize; +use serde_with::{DisplayFromStr, serde_as}; +use serde_json::Value; + +use fastcrypto::encoding::{Base64}; + +use anyhow::anyhow; + +use crate::move_core_types::{ + identifier::Identifier, + language_storage::StructTag +}; +use crate::types::{ + base_types::{ObjectID, SequenceNumber, ObjectType, ObjectRef, ObjectInfo, IotaAddress}, + move_package::{TypeOrigin, UpgradeInfo, MovePackage}, + iota_serde::{IotaStructTag, BigInt, SequenceNumber as AsSequenceNumber}, + digests::{ObjectDigest,TransactionDigest}, + object::Owner, + error::{IotaObjectResponseError, UserInputResult, UserInputError}, + gas_coin::GasCoin, +}; + +use super::{ + Page, + iota_move::{IotaMoveStruct, IotaMoveValue}, +}; + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct IotaObjectResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +impl IotaObjectResponse { + pub fn new(data: Option, error: Option) -> Self { + Self { data, error } + } + + pub fn new_with_data(data: IotaObjectData) -> Self { + Self { + data: Some(data), + error: None, + } + } + + pub fn new_with_error(error: IotaObjectResponseError) -> Self { + Self { + data: None, + error: Some(error), + } + } +} + +impl Ord for IotaObjectResponse { + fn cmp(&self, other: &Self) -> Ordering { + match (&self.data, &other.data) { + (Some(data), Some(data_2)) => { + if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Greater) { + return Ordering::Greater; + } else if data.object_id.cmp(&data_2.object_id).eq(&Ordering::Less) { + return Ordering::Less; + } + Ordering::Equal + } + // In this ordering those with data will come before IotaObjectResponses that are + // errors. + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, + // IotaObjectResponses that are errors are just considered equal. + _ => Ordering::Equal, + } + } +} + +impl PartialOrd for IotaObjectResponse { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl IotaObjectResponse { + pub fn move_object_bcs(&self) -> Option<&Vec> { + match &self.data { + Some(IotaObjectData { + bcs: Some(IotaRawData::MoveObject(obj)), + .. + }) => Some(&obj.bcs_bytes), + _ => None, + } + } + + pub fn owner(&self) -> Option { + if let Some(data) = &self.data { + return data.owner; + } + None + } + + pub fn object_id(&self) -> Result { + match (&self.data, &self.error) { + (Some(obj_data), None) => Ok(obj_data.object_id), + (None, Some(IotaObjectResponseError::NotExists { object_id })) => Ok(*object_id), + ( + None, + Some(IotaObjectResponseError::Deleted { + object_id, + version: _, + digest: _, + }), + ) => Ok(*object_id), + _ => Err(anyhow!( + "Could not get object_id, something went wrong with IotaObjectResponse construction." + )), + } + } + + pub fn object_ref_if_exists(&self) -> Option { + match (&self.data, &self.error) { + (Some(obj_data), None) => Some(obj_data.object_ref()), + _ => None, + } + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)] +pub struct DisplayFieldsResponse { + pub data: Option>, + pub error: Option, +} + +#[serde_as] +#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase", rename = "ObjectData")] +pub struct IotaObjectData { + pub object_id: ObjectID, + /// Object version. + #[serde_as(as = "AsSequenceNumber")] + pub version: SequenceNumber, + /// Base64 string representing the object digest + pub digest: ObjectDigest, + /// The type of the object. Default to be None unless + /// IotaObjectDataOptions.showType is set to true + #[serde_as(as = "Option")] + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub type_: Option, + // Default to be None because otherwise it will be repeated for the getOwnedObjects endpoint + /// The owner of this object. Default to be None unless + /// IotaObjectDataOptions.showOwner is set to true + #[serde(skip_serializing_if = "Option::is_none")] + pub owner: Option, + /// The digest of the transaction that created or last mutated this object. + /// Default to be None unless IotaObjectDataOptions. + /// showPreviousTransaction is set to true + #[serde(skip_serializing_if = "Option::is_none")] + pub previous_transaction: Option, + /// The amount of IOTA we would rebate if this object gets deleted. + /// This number is re-calculated each time the object is mutated based on + /// the present storage gas price. + #[serde_as(as = "Option>")] + #[serde(skip_serializing_if = "Option::is_none")] + pub storage_rebate: Option, + /// The Display metadata for frontend UI rendering, default to be None + /// unless IotaObjectDataOptions.showContent is set to true This can also + /// be None if the struct type does not have Display defined See more details in + #[serde(skip_serializing_if = "Option::is_none")] + pub display: Option, + /// Move object content or package content, default to be None unless + /// IotaObjectDataOptions.showContent is set to true + #[serde(skip_serializing_if = "Option::is_none")] + pub content: Option, + /// Move object content or package content in BCS, default to be None unless + /// IotaObjectDataOptions.showBcs is set to true + #[serde(skip_serializing_if = "Option::is_none")] + pub bcs: Option, +} + +impl IotaObjectData { + pub fn object_ref(&self) -> ObjectRef { + (self.object_id, self.version, self.digest) + } + + pub fn object_type(&self) -> anyhow::Result { + self.type_ + .as_ref() + .ok_or_else(|| anyhow!("type is missing for object {:?}", self.object_id)) + .cloned() + } + + pub fn is_gas_coin(&self) -> bool { + match self.type_.as_ref() { + Some(ObjectType::Struct(ty)) if ty.is_gas_coin() => true, + Some(_) => false, + None => false, + } + } +} + +impl Display for IotaObjectData { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let type_ = if let Some(type_) = &self.type_ { + type_.to_string() + } else { + "Unknown Type".into() + }; + let mut writer = String::new(); + writeln!( + writer, + "{}", + format!("----- {type_} ({}[{}]) -----", self.object_id, self.version) + )?; + if let Some(owner) = self.owner { + writeln!(writer, "{}: {owner}", "Owner")?; + } + + writeln!( + writer, + "{}: {}", + "Version", + self.version + )?; + if let Some(storage_rebate) = self.storage_rebate { + writeln!( + writer, + "{}: {storage_rebate}", + "Storage Rebate", + )?; + } + + if let Some(previous_transaction) = self.previous_transaction { + writeln!( + writer, + "{}: {previous_transaction:?}", + "Previous Transaction", + )?; + } + + write!(f, "{writer}") + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Default)] +#[serde(rename_all = "camelCase", rename = "ObjectDataOptions", default)] +pub struct IotaObjectDataOptions { + /// Whether to show the type of the object. Default to be False + pub show_type: bool, + /// Whether to show the owner of the object. Default to be False + pub show_owner: bool, + /// Whether to show the previous transaction digest of the object. Default + /// to be False + pub show_previous_transaction: bool, + /// Whether to show the Display metadata of the object for frontend + /// rendering. Default to be False + pub show_display: bool, + /// Whether to show the content(i.e., package content or Move struct + /// content) of the object. Default to be False + pub show_content: bool, + /// Whether to show the content in BCS format. Default to be False + pub show_bcs: bool, + /// Whether to show the storage rebate of the object. Default to be False + pub show_storage_rebate: bool, +} + +impl IotaObjectDataOptions { + pub fn new() -> Self { + Self::default() + } + + /// return BCS data and all other metadata such as storage rebate + pub fn bcs_lossless() -> Self { + Self { + show_bcs: true, + show_type: true, + show_owner: true, + show_previous_transaction: true, + show_display: false, + show_content: false, + show_storage_rebate: true, + } + } + + /// return full content except bcs + pub fn full_content() -> Self { + Self { + show_bcs: false, + show_type: true, + show_owner: true, + show_previous_transaction: true, + show_display: false, + show_content: true, + show_storage_rebate: true, + } + } + + pub fn with_content(mut self) -> Self { + self.show_content = true; + self + } + + pub fn with_owner(mut self) -> Self { + self.show_owner = true; + self + } + + pub fn with_type(mut self) -> Self { + self.show_type = true; + self + } + + pub fn with_display(mut self) -> Self { + self.show_display = true; + self + } + + pub fn with_bcs(mut self) -> Self { + self.show_bcs = true; + self + } + + pub fn with_previous_transaction(mut self) -> Self { + self.show_previous_transaction = true; + self + } + + pub fn is_not_in_object_info(&self) -> bool { + self.show_bcs || self.show_content || self.show_display || self.show_storage_rebate + } +} + +impl IotaObjectResponse { + /// Returns a reference to the object if there is any, otherwise an Err if + /// the object does not exist or is deleted. + pub fn object(&self) -> Result<&IotaObjectData, IotaObjectResponseError> { + if let Some(data) = &self.data { + Ok(data) + } else if let Some(error) = &self.error { + Err(error.clone()) + } else { + // We really shouldn't reach this code block since either data, or error field + // should always be filled. + Err(IotaObjectResponseError::Unknown) + } + } + + /// Returns the object value if there is any, otherwise an Err if + /// the object does not exist or is deleted. + pub fn into_object(self) -> Result { + match self.object() { + Ok(data) => Ok(data.clone()), + Err(error) => Err(error), + } + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd)] +#[serde(rename_all = "camelCase", rename = "ObjectRef")] +pub struct IotaObjectRef { + /// Hex code as string representing the object id + pub object_id: ObjectID, + /// Object version. + pub version: SequenceNumber, + /// Base64 string representing the object digest + pub digest: ObjectDigest, +} + +impl IotaObjectRef { + pub fn to_object_ref(&self) -> ObjectRef { + (self.object_id, self.version, self.digest) + } +} + +impl Display for IotaObjectRef { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "Object ID: {}, version: {}, digest: {}", + self.object_id, self.version, self.digest + ) + } +} + +impl From for IotaObjectRef { + fn from(oref: ObjectRef) -> Self { + Self { + object_id: oref.0, + version: oref.1, + digest: oref.2, + } + } +} + +pub trait IotaData: Sized { + type ObjectType; + type PackageType; + // Code is commented out because MoveObject and MoveStructLayout + // introduce too many depencies + // fn try_from_object(object: MoveObject, layout: MoveStructLayout) + // -> Result; + // fn try_from_package(package: MovePackage) -> Result; + fn try_as_move(&self) -> Option<&Self::ObjectType>; + fn try_into_move(self) -> Option; + fn try_as_package(&self) -> Option<&Self::PackageType>; + fn type_(&self) -> Option<&StructTag>; +} + +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(tag = "dataType", rename_all = "camelCase", rename = "RawData")] +pub enum IotaRawData { + // Manually handle generic schema generation + MoveObject(IotaRawMoveObject), + Package(IotaRawMovePackage), +} + +impl IotaData for IotaRawData { + type ObjectType = IotaRawMoveObject; + type PackageType = IotaRawMovePackage; + + fn try_as_move(&self) -> Option<&Self::ObjectType> { + match self { + Self::MoveObject(o) => Some(o), + Self::Package(_) => None, + } + } + + fn try_into_move(self) -> Option { + match self { + Self::MoveObject(o) => Some(o), + Self::Package(_) => None, + } + } + + fn try_as_package(&self) -> Option<&Self::PackageType> { + match self { + Self::MoveObject(_) => None, + Self::Package(p) => Some(p), + } + } + + fn type_(&self) -> Option<&StructTag> { + match self { + Self::MoveObject(o) => Some(&o.type_), + Self::Package(_) => None, + } + } +} + + +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(tag = "dataType", rename_all = "camelCase", rename = "Data")] +pub enum IotaParsedData { + // Manually handle generic schema generation + MoveObject(IotaParsedMoveObject), + Package(IotaMovePackage), +} + +impl IotaData for IotaParsedData { + type ObjectType = IotaParsedMoveObject; + type PackageType = IotaMovePackage; + + fn try_as_move(&self) -> Option<&Self::ObjectType> { + match self { + Self::MoveObject(o) => Some(o), + Self::Package(_) => None, + } + } + + fn try_into_move(self) -> Option { + match self { + Self::MoveObject(o) => Some(o), + Self::Package(_) => None, + } + } + + fn try_as_package(&self) -> Option<&Self::PackageType> { + match self { + Self::MoveObject(_) => None, + Self::Package(p) => Some(p), + } + } + + fn type_(&self) -> Option<&StructTag> { + match self { + Self::MoveObject(o) => Some(&o.type_), + Self::Package(_) => None, + } + } +} + +impl Display for IotaParsedData { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut writer = String::new(); + match self { + IotaParsedData::MoveObject(o) => { + writeln!(writer, "{}: {}", "type", o.type_)?; + write!(writer, "{}", &o.fields)?; + } + IotaParsedData::Package(p) => { + write!( + writer, + "{}: {:?}", + "Modules", + p.disassembled.keys() + )?; + } + } + write!(f, "{writer}") + } +} + +#[serde_as] +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(rename = "MoveObject", rename_all = "camelCase")] +pub struct IotaParsedMoveObject { + #[serde(rename = "type")] + #[serde_as(as = "IotaStructTag")] + pub type_: StructTag, + pub has_public_transfer: bool, + pub fields: IotaMoveStruct, +} + +impl IotaParsedMoveObject { + pub fn read_dynamic_field_value(&self, field_name: &str) -> Option { + match &self.fields { + IotaMoveStruct::WithFields(fields) => fields.get(field_name).cloned(), + IotaMoveStruct::WithTypes { fields, .. } => fields.get(field_name).cloned(), + _ => None, + } + } +} + +#[serde_as] +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(rename = "RawMoveObject", rename_all = "camelCase")] +pub struct IotaRawMoveObject { + #[serde(rename = "type")] + #[serde_as(as = "IotaStructTag")] + pub type_: StructTag, + pub has_public_transfer: bool, + pub version: SequenceNumber, + #[serde_as(as = "Base64")] + pub bcs_bytes: Vec, +} + +impl IotaRawMoveObject { + pub fn deserialize<'a, T: Deserialize<'a>>(&'a self) -> Result { + Ok(bcs::from_bytes(self.bcs_bytes.as_slice())?) + } +} + +#[serde_as] +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(rename = "RawMovePackage", rename_all = "camelCase")] +pub struct IotaRawMovePackage { + pub id: ObjectID, + pub version: SequenceNumber, + #[serde_as(as = "BTreeMap<_, Base64>")] + pub module_map: BTreeMap>, + pub type_origin_table: Vec, + pub linkage_table: BTreeMap, +} + +impl From for IotaRawMovePackage { + fn from(p: MovePackage) -> Self { + Self { + id: p.id(), + version: p.version(), + module_map: p.serialized_module_map().clone(), + type_origin_table: p.type_origin_table().clone(), + linkage_table: p.linkage_table().clone(), + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(tag = "status", content = "details", rename = "ObjectRead")] +pub enum IotaPastObjectResponse { + /// The object exists and is found with this version + VersionFound(IotaObjectData), + /// The object does not exist + ObjectNotExists(ObjectID), + /// The object is found to be deleted with this version + ObjectDeleted(IotaObjectRef), + /// The object exists but not found with this version + VersionNotFound(ObjectID, SequenceNumber), + /// The asked object version is higher than the latest + VersionTooHigh { + object_id: ObjectID, + asked_version: SequenceNumber, + latest_version: SequenceNumber, + }, +} + +impl IotaPastObjectResponse { + /// Returns a reference to the object if there is any, otherwise an Err + pub fn object(&self) -> UserInputResult<&IotaObjectData> { + match &self { + Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { + object_ref: oref.to_object_ref(), + }), + Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound { + object_id: *id, + version: None, + }), + Self::VersionFound(o) => Ok(o), + Self::VersionNotFound(id, seq_num) => Err(UserInputError::ObjectNotFound { + object_id: *id, + version: Some(*seq_num), + }), + Self::VersionTooHigh { + object_id, + asked_version, + latest_version, + } => Err(UserInputError::ObjectSequenceNumberTooHigh { + object_id: *object_id, + asked_version: *asked_version, + latest_version: *latest_version, + }), + } + } + + /// Returns the object value if there is any, otherwise an Err + pub fn into_object(self) -> UserInputResult { + match self { + Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { + object_ref: oref.to_object_ref(), + }), + Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound { + object_id: id, + version: None, + }), + Self::VersionFound(o) => Ok(o), + Self::VersionNotFound(object_id, version) => Err(UserInputError::ObjectNotFound { + object_id, + version: Some(version), + }), + Self::VersionTooHigh { + object_id, + asked_version, + latest_version, + } => Err(UserInputError::ObjectSequenceNumberTooHigh { + object_id, + asked_version, + latest_version, + }), + } + } +} + +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(rename = "MovePackage", rename_all = "camelCase")] +pub struct IotaMovePackage { + pub disassembled: BTreeMap, +} + +pub type ObjectsPage = Page; + +#[serde_as] +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[serde(rename = "GetPastObjectRequest", rename_all = "camelCase")] +pub struct IotaGetPastObjectRequest { + /// the ID of the queried object + pub object_id: ObjectID, + /// the version of the queried object. + #[serde_as(as = "AsSequenceNumber")] + pub version: SequenceNumber, +} + +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum IotaObjectDataFilter { + MatchAll(Vec), + MatchAny(Vec), + MatchNone(Vec), + /// Query by type a specified Package. + Package(ObjectID), + /// Query by type a specified Move module. + MoveModule { + /// the Move package ID + package: ObjectID, + /// the module name + #[serde_as(as = "DisplayFromStr")] + module: Identifier, + }, + /// Query by type + StructType( + #[serde_as(as = "IotaStructTag")] + StructTag, + ), + AddressOwner(IotaAddress), + ObjectOwner(ObjectID), + ObjectId(ObjectID), + // allow querying for multiple object ids + ObjectIds(Vec), + Version( + #[serde_as(as = "BigInt")] + u64, + ), +} + +impl IotaObjectDataFilter { + pub fn gas_coin() -> Self { + Self::StructType(GasCoin::type_()) + } + + pub fn and(self, other: Self) -> Self { + Self::MatchAll(vec![self, other]) + } + pub fn or(self, other: Self) -> Self { + Self::MatchAny(vec![self, other]) + } + pub fn not(self, other: Self) -> Self { + Self::MatchNone(vec![self, other]) + } + + pub fn matches(&self, object: &ObjectInfo) -> bool { + match self { + IotaObjectDataFilter::MatchAll(filters) => !filters.iter().any(|f| !f.matches(object)), + IotaObjectDataFilter::MatchAny(filters) => filters.iter().any(|f| f.matches(object)), + IotaObjectDataFilter::MatchNone(filters) => !filters.iter().any(|f| f.matches(object)), + IotaObjectDataFilter::StructType(s) => { + let obj_tag: StructTag = match &object.type_ { + ObjectType::Package => return false, + ObjectType::Struct(s) => s.clone().into(), + }; + // If people do not provide type_params, we will match all type_params + // e.g. `0x2::coin::Coin` can match `0x2::coin::Coin<0x2::iota::IOTA>` + if !s.type_params.is_empty() && s.type_params != obj_tag.type_params { + false + } else { + obj_tag.address == s.address + && obj_tag.module == s.module + && obj_tag.name == s.name + } + } + IotaObjectDataFilter::MoveModule { package, module } => { + matches!(&object.type_, ObjectType::Struct(s) if &ObjectID::from(s.address()) == package + && s.module() == module.as_ident_str()) + } + IotaObjectDataFilter::Package(p) => { + matches!(&object.type_, ObjectType::Struct(s) if &ObjectID::from(s.address()) == p) + } + IotaObjectDataFilter::AddressOwner(a) => { + matches!(object.owner, Owner::AddressOwner(addr) if &addr == a) + } + IotaObjectDataFilter::ObjectOwner(o) => { + matches!(object.owner, Owner::ObjectOwner(addr) if addr == IotaAddress::from(*o)) + } + IotaObjectDataFilter::ObjectId(id) => &object.object_id == id, + IotaObjectDataFilter::ObjectIds(ids) => ids.contains(&object.object_id), + IotaObjectDataFilter::Version(v) => object.version.value() == *v, + } + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, Default)] +#[serde(rename_all = "camelCase", rename = "ObjectResponseQuery", default)] +pub struct IotaObjectResponseQuery { + /// If None, no filter will be applied + pub filter: Option, + /// config which fields to include in the response, by default only digest + /// is included + pub options: Option, +} + +impl IotaObjectResponseQuery { + pub fn new( + filter: Option, + options: Option, + ) -> Self { + Self { filter, options } + } + + pub fn new_with_filter(filter: IotaObjectDataFilter) -> Self { + Self { + filter: Some(filter), + options: None, + } + } + + pub fn new_with_options(options: IotaObjectDataOptions) -> Self { + Self { + filter: None, + options: Some(options), + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs new file mode 100644 index 0000000000..123ed012a4 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs @@ -0,0 +1,198 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::vec::Vec; + +use serde::Deserialize; +use serde::Serialize; + +use crate::types::{ + base_types::{SequenceNumber, ObjectID}, + object::Owner, + quorum_driver_types::ExecuteTransactionRequestType, + execution_status::ExecutionStatus, +}; + +use super::iota_object::IotaObjectRef; + +/// BCS serialized IotaTransactionBlockEffects +pub type IotaTransactionBlockEffectsBcs = Vec; + +/// BCS serialized IotaTransactionBlockEvents +pub type IotaTransactionBlockEventsBcs = Vec; + +/// BCS serialized ObjectChange +pub type ObjectChangeBcs = Vec; + +/// BCS serialized BalanceChange +pub type BalanceChangeBcs = Vec; + +/// BCS serialized IotaTransactionBlockKind +pub type IotaTransactionBlockKindBcs = Vec; + +pub type CheckpointSequenceNumber = u64; + +#[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Default)] +#[serde( +rename_all = "camelCase", +rename = "TransactionBlockResponseOptions", +default +)] +pub struct IotaTransactionBlockResponseOptions { + /// Whether to show transaction input data. Default to be False + pub show_input: bool, + /// Whether to show bcs-encoded transaction input data + pub show_raw_input: bool, + /// Whether to show transaction effects. Default to be False + pub show_effects: bool, + /// Whether to show transaction events. Default to be False + pub show_events: bool, + /// Whether to show object_changes. Default to be False + pub show_object_changes: bool, + /// Whether to show balance_changes. Default to be False + pub show_balance_changes: bool, + /// Whether to show raw transaction effects. Default to be False + pub show_raw_effects: bool, +} + +impl IotaTransactionBlockResponseOptions { + pub fn new() -> Self { + Self::default() + } + + pub fn full_content() -> Self { + Self { + show_effects: true, + show_input: true, + show_raw_input: true, + show_events: true, + show_object_changes: true, + show_balance_changes: true, + // This field is added for graphql execution. We keep it false here + // so current users of `full_content` will not get raw effects unexpectedly. + show_raw_effects: false, + } + } + + pub fn with_input(mut self) -> Self { + self.show_input = true; + self + } + + pub fn with_raw_input(mut self) -> Self { + self.show_raw_input = true; + self + } + + pub fn with_effects(mut self) -> Self { + self.show_effects = true; + self + } + + pub fn with_events(mut self) -> Self { + self.show_events = true; + self + } + + pub fn with_balance_changes(mut self) -> Self { + self.show_balance_changes = true; + self + } + + pub fn with_object_changes(mut self) -> Self { + self.show_object_changes = true; + self + } + + pub fn with_raw_effects(mut self) -> Self { + self.show_raw_effects = true; + self + } + + /// default to return `WaitForEffectsCert` unless some options require + /// local execution + pub fn default_execution_request_type(&self) -> ExecuteTransactionRequestType { + // if people want effects or events, they typically want to wait for local + // execution + if self.require_effects() { + ExecuteTransactionRequestType::WaitForLocalExecution + } else { + ExecuteTransactionRequestType::WaitForEffectsCert + } + } + + pub fn require_local_execution(&self) -> bool { + self.show_balance_changes || self.show_object_changes + } + + pub fn require_input(&self) -> bool { + self.show_input || self.show_raw_input || self.show_object_changes + } + + pub fn require_effects(&self) -> bool { + self.show_effects + || self.show_events + || self.show_balance_changes + || self.show_object_changes + || self.show_raw_effects + } + + pub fn only_digest(&self) -> bool { + self == &Self::default() + } +} + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +#[serde(rename = "ExecutionStatus", rename_all = "camelCase", tag = "status")] +pub enum IotaExecutionStatus { + // Gas used in the success case. + Success, + // Gas used in the failed case, and the error. + Failure { error: String }, +} + +impl IotaExecutionStatus { + pub fn is_ok(&self) -> bool { + matches!(self, IotaExecutionStatus::Success { .. }) + } + pub fn is_err(&self) -> bool { + matches!(self, IotaExecutionStatus::Failure { .. }) + } +} + +impl From for IotaExecutionStatus { + fn from(status: ExecutionStatus) -> Self { + match status { + ExecutionStatus::Success => Self::Success, + ExecutionStatus::Failure { + error, + command: None, + } => Self::Failure { + error: format!("{error:?}"), + }, + ExecutionStatus::Failure { + error, + command: Some(idx), + } => Self::Failure { + error: format!("{error:?} in command {idx}"), + }, + } + } +} + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +#[serde(rename = "OwnedObjectRef")] +pub struct OwnedObjectRef { + pub owner: Owner, + pub reference: IotaObjectRef, +} + +impl OwnedObjectRef { + pub fn object_id(&self) -> ObjectID { + self.reference.object_id + } + pub fn version(&self) -> SequenceNumber { + self.reference.version + } +} diff --git a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/mod.rs b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/mod.rs new file mode 100644 index 0000000000..47aa47d99e --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/mod.rs @@ -0,0 +1,27 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub mod iota_transaction; +pub mod iota_object; +pub mod iota_coin; +pub mod iota_event; +pub mod iota_move; + +pub use iota_transaction::*; +pub use iota_object::*; +pub use iota_coin::*; +pub use iota_event::*; + +use serde::{Deserialize, Serialize}; + +/// `next_cursor` points to the last item in the page; +/// Reading with `next_cursor` will start from the next item after `next_cursor` +/// if `next_cursor` is `Some`, otherwise it will start from the first item. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Page { + pub data: Vec, + pub next_cursor: Option, + pub has_next_page: bool, +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/balance.rs b/identity_iota_interaction/src/sdk_types/iota_types/balance.rs new file mode 100644 index 0000000000..c4867a4a10 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/balance.rs @@ -0,0 +1,95 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ident_str, fp_ensure}; + +use super::super::move_core_types::{ + identifier::{IdentStr}, + language_storage::{StructTag, TypeTag}, + annotated_value::{MoveStructLayout, MoveFieldLayout, MoveTypeLayout} +}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; + +use super::{ + error::{ExecutionError, ExecutionErrorKind}, + iota_serde::{BigInt, Readable}, + IOTA_FRAMEWORK_ADDRESS, +}; + +pub const BALANCE_MODULE_NAME: &IdentStr = ident_str!("balance"); +pub const BALANCE_STRUCT_NAME: &IdentStr = ident_str!("Balance"); +pub const BALANCE_CREATE_REWARDS_FUNCTION_NAME: &IdentStr = ident_str!("create_staking_rewards"); +pub const BALANCE_DESTROY_REBATES_FUNCTION_NAME: &IdentStr = ident_str!("destroy_storage_rebates"); + +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct Supply { + #[serde_as(as = "Readable, _>")] + pub value: u64, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct Balance { + value: u64, +} + +impl Balance { + pub fn new(value: u64) -> Self { + Self { value } + } + + pub fn type_(type_param: TypeTag) -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + module: BALANCE_MODULE_NAME.to_owned(), + name: BALANCE_STRUCT_NAME.to_owned(), + type_params: vec![type_param], + } + } + + pub fn type_tag(inner_type_param: TypeTag) -> TypeTag { + TypeTag::Struct(Box::new(Self::type_(inner_type_param))) + } + + pub fn is_balance(s: &StructTag) -> bool { + s.address == IOTA_FRAMEWORK_ADDRESS + && s.module.as_ident_str() == BALANCE_MODULE_NAME + && s.name.as_ident_str() == BALANCE_STRUCT_NAME + } + + pub fn withdraw(&mut self, amount: u64) -> Result<(), ExecutionError> { + fp_ensure!( + self.value >= amount, + ExecutionError::new_with_source( + ExecutionErrorKind::InsufficientCoinBalance, + format!("balance: {} required: {}", self.value, amount) + ) + ); + self.value -= amount; + Ok(()) + } + + pub fn deposit_for_safe_mode(&mut self, amount: u64) { + self.value += amount; + } + + pub fn value(&self) -> u64 { + self.value + } + + pub fn to_bcs_bytes(&self) -> Vec { + bcs::to_bytes(&self).unwrap() + } + + pub fn layout(type_param: TypeTag) -> MoveStructLayout { + MoveStructLayout { + type_: Self::type_(type_param), + fields: vec![MoveFieldLayout::new( + ident_str!("value").to_owned(), + MoveTypeLayout::U64, + )], + } + } +} diff --git a/identity_iota_interaction/src/sdk_types/iota_types/base_types.rs b/identity_iota_interaction/src/sdk_types/iota_types/base_types.rs new file mode 100644 index 0000000000..787a298273 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/base_types.rs @@ -0,0 +1,799 @@ +// Copyright (c) 2021, Facebook, Inc. and its affiliates +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt; +use std::vec::Vec; +use std::option::Option; +use std::convert::{AsRef, TryFrom}; +use std::result::Result::Ok; +use std::option::Option::Some; +use std::str::FromStr; +use std::string::String; + +use Result; + +use rand::Rng; +use anyhow::anyhow; + +use serde::{ser::Error, Deserialize, Serialize}; +use serde_with::serde_as; + +use fastcrypto::encoding::{Hex, Encoding, decode_bytes_hex}; +use fastcrypto::hash::HashFunction; + +use crate::ident_str; + +use super::super::move_core_types::language_storage::{StructTag, TypeTag, ModuleId}; +use super::super::move_core_types::identifier::IdentStr; +use super::super::move_core_types::account_address::AccountAddress; + +use super::{IOTA_FRAMEWORK_ADDRESS, IOTA_CLOCK_OBJECT_ID, IOTA_SYSTEM_ADDRESS, MOVE_STDLIB_ADDRESS}; +use super::balance::Balance; +use super::coin::{Coin, CoinMetadata, TreasuryCap, COIN_MODULE_NAME, COIN_STRUCT_NAME}; +use super::crypto::{AuthorityPublicKeyBytes, IotaPublicKey, DefaultHash, PublicKey}; +use super::dynamic_field::DynamicFieldInfo; +use super::error::{IotaError, IotaResult}; +use super::gas_coin::GAS; +use super::governance::{StakedIota, STAKING_POOL_MODULE_NAME, STAKED_IOTA_STRUCT_NAME}; +use super::iota_serde::{Readable, HexAccountAddress, parse_iota_struct_tag, to_iota_struct_tag_string}; +use super::timelock::timelock::{self, TimeLock, TimelockedStakedIota}; +use super::stardust::nft::Nft; +use super::gas_coin::GasCoin; +use super::object::{Owner}; + +pub use super::digests::{ObjectDigest, TransactionDigest}; + +pub type EpochId = u64; + +// TODO: the stake and voting power of a validator can be different so +// in some places when we are actually referring to the voting power, we +// should use a different type alias, field name, etc. +pub type StakeUnit = u64; + +pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest); + +pub type AuthorityName = AuthorityPublicKeyBytes; + +pub type CommandIndex = usize; + +/// Type parameters are encoded as indices. This index can also be used to +/// lookup the kind of a type parameter in the `FunctionHandle` and +/// `StructHandle`. +pub type TypeParameterIndex = u16; +pub type CodeOffset = u16; + +#[derive( + Eq, + PartialEq, + Ord, + PartialOrd, + Copy, + Clone, + Hash, + Default, + Debug, + Serialize, + Deserialize, +)] +pub struct SequenceNumber(u64); + +impl fmt::Display for SequenceNumber { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#x}", self.0) + } +} + +// TODO: rename to version +impl SequenceNumber { + pub const MIN: SequenceNumber = SequenceNumber(u64::MIN); + pub const MAX: SequenceNumber = SequenceNumber(0x7fff_ffff_ffff_ffff); + + pub const fn new() -> Self { + SequenceNumber(0) + } + + pub const fn value(&self) -> u64 { + self.0 + } + + pub const fn from_u64(u: u64) -> Self { + SequenceNumber(u) + } +} + +#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)] +pub enum ObjectIDParseError { + #[error("ObjectID hex literal must start with 0x")] + HexLiteralPrefixMissing, + + #[error("Could not convert from bytes slice")] + TryFromSliceError, +} + +#[serde_as] +#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct ObjectID( + #[serde_as(as = "Readable")] + AccountAddress, +); + +impl ObjectID { + /// The number of bytes in an address. + pub const LENGTH: usize = AccountAddress::LENGTH; + /// Hex address: 0x0 + pub const ZERO: Self = Self::new([0u8; Self::LENGTH]); + pub const MAX: Self = Self::new([0xff; Self::LENGTH]); + /// Create a new ObjectID + pub const fn new(obj_id: [u8; Self::LENGTH]) -> Self { + Self(AccountAddress::new(obj_id)) + } + + /// Const fn variant of `>::from` + pub const fn from_address(addr: AccountAddress) -> Self { + Self(addr) + } + + /// Return a random ObjectID. + pub fn random() -> Self { + Self::from(AccountAddress::random()) + } + + /// Return the underlying bytes buffer of the ObjectID. + pub fn to_vec(&self) -> Vec { + self.0.to_vec() + } + + /// Parse the ObjectID from byte array or buffer. + pub fn from_bytes>(bytes: T) -> Result { + <[u8; Self::LENGTH]>::try_from(bytes.as_ref()) + .map_err(|_| ObjectIDParseError::TryFromSliceError) + .map(ObjectID::new) + } + + /// Return the underlying bytes array of the ObjectID. + pub fn into_bytes(self) -> [u8; Self::LENGTH] { + self.0.into_bytes() + } + + /// Make an ObjectID with padding 0s before the single byte. + pub const fn from_single_byte(byte: u8) -> ObjectID { + let mut bytes = [0u8; Self::LENGTH]; + bytes[Self::LENGTH - 1] = byte; + ObjectID::new(bytes) + } + + /// Convert from hex string to ObjectID where the string is prefixed with 0x + /// Padding 0s if the string is too short. + pub fn from_hex_literal(literal: &str) -> Result { + if !literal.starts_with("0x") { + return Err(ObjectIDParseError::HexLiteralPrefixMissing); + } + + let hex_len = literal.len() - 2; + + // If the string is too short, pad it + if hex_len < Self::LENGTH * 2 { + let mut hex_str = String::with_capacity(Self::LENGTH * 2); + for _ in 0..Self::LENGTH * 2 - hex_len { + hex_str.push('0'); + } + hex_str.push_str(&literal[2..]); + Self::from_str(&hex_str) + } else { + Self::from_str(&literal[2..]) + } + } + + + /// Return the full hex string with 0x prefix without removing trailing 0s. + /// Prefer this over [fn to_hex_literal] if the string needs to be fully + /// preserved. + pub fn to_hex_uncompressed(&self) -> String { + format!("{self}") + } + + pub fn is_clock(&self) -> bool { + *self == IOTA_CLOCK_OBJECT_ID + } +} + +impl From for ObjectID { + fn from(address: IotaAddress) -> ObjectID { + let tmp: AccountAddress = address.into(); + tmp.into() + } +} + +impl From for ObjectID { + fn from(address: AccountAddress) -> Self { + Self(address) + } +} + +impl fmt::Display for ObjectID { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "0x{}", Hex::encode(self.0)) + } +} + +impl fmt::Debug for ObjectID { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "0x{}", Hex::encode(self.0)) + } +} + +impl AsRef<[u8]> for ObjectID { + fn as_ref(&self) -> &[u8] { + self.0.as_slice() + } +} + +impl TryFrom<&[u8]> for ObjectID { + type Error = ObjectIDParseError; + + /// Tries to convert the provided byte array into ObjectID. + fn try_from(bytes: &[u8]) -> Result { + Self::from_bytes(bytes) + } +} + +impl TryFrom> for ObjectID { + type Error = ObjectIDParseError; + + /// Tries to convert the provided byte buffer into ObjectID. + fn try_from(bytes: Vec) -> Result { + Self::from_bytes(bytes) + } +} + +impl FromStr for ObjectID { + type Err = ObjectIDParseError; + + /// Parse ObjectID from hex string with or without 0x prefix, pad with 0s if + /// needed. + fn from_str(s: &str) -> Result { + decode_bytes_hex(s).or_else(|_| Self::from_hex_literal(s)) + } +} + +impl std::ops::Deref for ObjectID { + type Target = AccountAddress; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// Wrapper around StructTag with a space-efficient representation for common +/// types like coins The StructTag for a gas coin is 84 bytes, so using 1 byte +/// instead is a win. The inner representation is private to prevent incorrectly +/// constructing an `Other` instead of one of the specialized variants, e.g. +/// `Other(GasCoin::type_())` instead of `GasCoin` +#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)] +pub struct MoveObjectType(MoveObjectType_); + +/// Even though it is declared public, it is the "private", internal +/// representation for `MoveObjectType` +#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Deserialize, Serialize, Hash)] +pub enum MoveObjectType_ { + /// A type that is not `0x2::coin::Coin` + Other(StructTag), + /// A IOTA coin (i.e., `0x2::coin::Coin<0x2::iota::IOTA>`) + GasCoin, + /// A record of a staked IOTA coin (i.e., `0x3::staking_pool::StakedIota`) + StakedIota, + /// A non-IOTA coin type (i.e., `0x2::coin::Coin where T != + /// 0x2::iota::IOTA`) + Coin(TypeTag), + // NOTE: if adding a new type here, and there are existing on-chain objects of that + // type with Other(_), that is ok, but you must hand-roll PartialEq/Eq/Ord/maybe Hash + // to make sure the new type and Other(_) are interpreted consistently. +} + +impl MoveObjectType { + pub fn gas_coin() -> Self { + Self(MoveObjectType_::GasCoin) + } + + pub fn staked_iota() -> Self { + Self(MoveObjectType_::StakedIota) + } + + pub fn timelocked_iota_balance() -> Self { + Self(MoveObjectType_::Other(TimeLock::::type_( + Balance::type_(GAS::type_().into()).into(), + ))) + } + + pub fn timelocked_staked_iota() -> Self { + Self(MoveObjectType_::Other(TimelockedStakedIota::type_())) + } + + pub fn stardust_nft() -> Self { + Self(MoveObjectType_::Other(Nft::tag())) + } + + pub fn address(&self) -> AccountAddress { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => IOTA_FRAMEWORK_ADDRESS, + MoveObjectType_::StakedIota => IOTA_SYSTEM_ADDRESS, + MoveObjectType_::Other(s) => s.address, + } + } + + pub fn module(&self) -> &IdentStr { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_MODULE_NAME, + MoveObjectType_::StakedIota => STAKING_POOL_MODULE_NAME, + MoveObjectType_::Other(s) => &s.module, + } + } + + pub fn name(&self) -> &IdentStr { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => COIN_STRUCT_NAME, + MoveObjectType_::StakedIota => STAKED_IOTA_STRUCT_NAME, + MoveObjectType_::Other(s) => &s.name, + } + } + + pub fn type_params(&self) -> Vec { + match &self.0 { + MoveObjectType_::GasCoin => vec![GAS::type_tag()], + MoveObjectType_::StakedIota => vec![], + MoveObjectType_::Coin(inner) => vec![inner.clone()], + MoveObjectType_::Other(s) => s.type_params.clone(), + } + } + + pub fn into_type_params(self) -> Vec { + match self.0 { + MoveObjectType_::GasCoin => vec![GAS::type_tag()], + MoveObjectType_::StakedIota => vec![], + MoveObjectType_::Coin(inner) => vec![inner], + MoveObjectType_::Other(s) => s.type_params, + } + } + + pub fn coin_type_maybe(&self) -> Option { + match &self.0 { + MoveObjectType_::GasCoin => Some(GAS::type_tag()), + MoveObjectType_::Coin(inner) => Some(inner.clone()), + MoveObjectType_::StakedIota => None, + MoveObjectType_::Other(_) => None, + } + } + + pub fn module_id(&self) -> ModuleId { + ModuleId::new(self.address(), self.module().to_owned()) + } + + pub fn size_for_gas_metering(&self) -> usize { + // unwraps safe because a `StructTag` cannot fail to serialize + match &self.0 { + MoveObjectType_::GasCoin => 1, + MoveObjectType_::StakedIota => 1, + MoveObjectType_::Coin(inner) => bcs::serialized_size(inner).unwrap() + 1, + MoveObjectType_::Other(s) => bcs::serialized_size(s).unwrap() + 1, + } + } + + /// Return true if `self` is `0x2::coin::Coin` for some T (note: T can be + /// IOTA) + pub fn is_coin(&self) -> bool { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) => true, + MoveObjectType_::StakedIota | MoveObjectType_::Other(_) => false, + } + } + + /// Return true if `self` is 0x2::coin::Coin<0x2::iota::IOTA> + pub fn is_gas_coin(&self) -> bool { + match &self.0 { + MoveObjectType_::GasCoin => true, + MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) | MoveObjectType_::Other(_) => { + false + } + } + } + + /// Return true if `self` is `0x2::coin::Coin` + pub fn is_coin_t(&self, t: &TypeTag) -> bool { + match &self.0 { + MoveObjectType_::GasCoin => GAS::is_gas_type(t), + MoveObjectType_::Coin(c) => t == c, + MoveObjectType_::StakedIota | MoveObjectType_::Other(_) => false, + } + } + + pub fn is_staked_iota(&self) -> bool { + match &self.0 { + MoveObjectType_::StakedIota => true, + MoveObjectType_::GasCoin | MoveObjectType_::Coin(_) | MoveObjectType_::Other(_) => { + false + } + } + } + + pub fn is_coin_metadata(&self) -> bool { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => { + false + } + MoveObjectType_::Other(s) => CoinMetadata::is_coin_metadata(s), + } + } + + pub fn is_treasury_cap(&self) -> bool { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => { + false + } + MoveObjectType_::Other(s) => TreasuryCap::is_treasury_type(s), + } + } + + pub fn is_upgrade_cap(&self) -> bool { + self.address() == IOTA_FRAMEWORK_ADDRESS + && self.module().as_str() == "package" + && self.name().as_str() == "UpgradeCap" + } + + pub fn is_regulated_coin_metadata(&self) -> bool { + self.address() == IOTA_FRAMEWORK_ADDRESS + && self.module().as_str() == "coin" + && self.name().as_str() == "RegulatedCoinMetadata" + } + + pub fn is_coin_deny_cap(&self) -> bool { + self.address() == IOTA_FRAMEWORK_ADDRESS + && self.module().as_str() == "coin" + && self.name().as_str() == "DenyCap" + } + + pub fn is_dynamic_field(&self) -> bool { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => { + false + } + MoveObjectType_::Other(s) => DynamicFieldInfo::is_dynamic_field(s), + } + } + + pub fn is_timelock(&self) -> bool { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => { + false + } + MoveObjectType_::Other(s) => timelock::is_timelock(s), + } + } + + pub fn is_timelocked_balance(&self) -> bool { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => { + false + } + MoveObjectType_::Other(s) => timelock::is_timelocked_balance(s), + } + } + + pub fn is_timelocked_staked_iota(&self) -> bool { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => { + false + } + MoveObjectType_::Other(s) => TimelockedStakedIota::is_timelocked_staked_iota(s), + } + } + + pub fn try_extract_field_value(&self) -> IotaResult { + match &self.0 { + MoveObjectType_::GasCoin | MoveObjectType_::StakedIota | MoveObjectType_::Coin(_) => { + Err(IotaError::ObjectDeserialization { + error: "Error extracting dynamic object value from Coin object".to_string(), + }) + } + MoveObjectType_::Other(s) => DynamicFieldInfo::try_extract_field_value(s), + } + } +} + +impl From for MoveObjectType { + fn from(mut s: StructTag) -> Self { + Self(if GasCoin::is_gas_coin(&s) { + MoveObjectType_::GasCoin + } else if Coin::is_coin(&s) { + // unwrap safe because a coin has exactly one type parameter + MoveObjectType_::Coin(s.type_params.pop().unwrap()) + } else if StakedIota::is_staked_iota(&s) { + MoveObjectType_::StakedIota + } else { + MoveObjectType_::Other(s) + }) + } +} + +impl From for StructTag { + fn from(t: MoveObjectType) -> Self { + match t.0 { + MoveObjectType_::GasCoin => GasCoin::type_(), + MoveObjectType_::StakedIota => StakedIota::type_(), + MoveObjectType_::Coin(inner) => Coin::type_(inner), + MoveObjectType_::Other(s) => s, + } + } +} + +impl From for TypeTag { + fn from(o: MoveObjectType) -> TypeTag { + let s: StructTag = o.into(); + TypeTag::Struct(Box::new(s)) + } +} + +/// Type of a Iota object +#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)] +pub enum ObjectType { + /// Move package containing one or more bytecode modules + Package, + /// A Move struct of the given type + Struct(MoveObjectType), +} + +impl TryFrom for StructTag { + type Error = anyhow::Error; + + fn try_from(o: ObjectType) -> Result { + match o { + ObjectType::Package => Err(anyhow!("Cannot create StructTag from Package")), + ObjectType::Struct(move_object_type) => Ok(move_object_type.into()), + } + } +} + +impl FromStr for ObjectType { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + if s.to_lowercase() == PACKAGE { + Ok(ObjectType::Package) + } else { + let tag = parse_iota_struct_tag(s)?; + Ok(ObjectType::Struct(MoveObjectType::from(tag))) + } + } +} + +#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)] +pub struct ObjectInfo { + pub object_id: ObjectID, + pub version: SequenceNumber, + pub digest: ObjectDigest, + pub type_: ObjectType, + pub owner: Owner, + pub previous_transaction: TransactionDigest, +} + +const PACKAGE: &str = "package"; +impl ObjectType { + pub fn is_gas_coin(&self) -> bool { + matches!(self, ObjectType::Struct(s) if s.is_gas_coin()) + } + + pub fn is_coin(&self) -> bool { + matches!(self, ObjectType::Struct(s) if s.is_coin()) + } + + /// Return true if `self` is `0x2::coin::Coin` + pub fn is_coin_t(&self, t: &TypeTag) -> bool { + matches!(self, ObjectType::Struct(s) if s.is_coin_t(t)) + } + + pub fn is_package(&self) -> bool { + matches!(self, ObjectType::Package) + } +} + +impl From for ObjectRef { + fn from(info: ObjectInfo) -> Self { + (info.object_id, info.version, info.digest) + } +} + +impl From<&ObjectInfo> for ObjectRef { + fn from(info: &ObjectInfo) -> Self { + (info.object_id, info.version, info.digest) + } +} + +pub const IOTA_ADDRESS_LENGTH: usize = ObjectID::LENGTH; + +#[serde_as] +#[derive(Eq, Default, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)] +pub struct IotaAddress( + #[serde_as(as = "Readable")] + [u8; IOTA_ADDRESS_LENGTH], +); + +impl IotaAddress { + pub const ZERO: Self = Self([0u8; IOTA_ADDRESS_LENGTH]); + + /// Convert the address to a byte buffer. + pub fn to_vec(&self) -> Vec { + self.0.to_vec() + } + + pub fn generate(mut rng: R) -> Self { + let buf: [u8; IOTA_ADDRESS_LENGTH] = rng.gen(); + Self(buf) + } + + /// Serialize an `Option` in Hex. + pub fn optional_address_as_hex( + key: &Option, + serializer: S, + ) -> Result + where + S: serde::ser::Serializer, + { + serializer.serialize_str(&key.map(Hex::encode).unwrap_or_default()) + } + + /// Deserialize into an `Option`. + pub fn optional_address_from_hex<'de, D>( + deserializer: D, + ) -> Result, D::Error> + where + D: serde::de::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + let value = decode_bytes_hex(&s).map_err(serde::de::Error::custom)?; + Ok(Some(value)) + } + + /// Return the underlying byte array of a IotaAddress. + pub fn to_inner(self) -> [u8; IOTA_ADDRESS_LENGTH] { + self.0 + } + + /// Parse a IotaAddress from a byte array or buffer. + pub fn from_bytes>(bytes: T) -> Result { + <[u8; IOTA_ADDRESS_LENGTH]>::try_from(bytes.as_ref()) + .map_err(|_| IotaError::InvalidAddress) + .map(IotaAddress) + } +} + +impl From for IotaAddress { + fn from(object_id: ObjectID) -> IotaAddress { + Self(object_id.into_bytes()) + } +} + +impl From for IotaAddress { + fn from(address: AccountAddress) -> IotaAddress { + Self(address.into_bytes()) + } +} + +impl TryFrom<&[u8]> for IotaAddress { + type Error = IotaError; + + /// Tries to convert the provided byte array into a IotaAddress. + fn try_from(bytes: &[u8]) -> Result { + Self::from_bytes(bytes) + } +} + +impl TryFrom> for IotaAddress { + type Error = IotaError; + + /// Tries to convert the provided byte buffer into a IotaAddress. + fn try_from(bytes: Vec) -> Result { + Self::from_bytes(bytes) + } +} + +impl AsRef<[u8]> for IotaAddress { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl FromStr for IotaAddress { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result { + decode_bytes_hex(s).map_err(|e| anyhow!(e)) + } +} + +impl From<&T> for IotaAddress { + fn from(pk: &T) -> Self { + let mut hasher = DefaultHash::default(); + T::SIGNATURE_SCHEME.update_hasher_with_flag(&mut hasher); + hasher.update(pk); + let g_arr = hasher.finalize(); + IotaAddress(g_arr.digest) + } +} + +impl From<&PublicKey> for IotaAddress { + fn from(pk: &PublicKey) -> Self { + let mut hasher = DefaultHash::default(); + pk.scheme().update_hasher_with_flag(&mut hasher); + hasher.update(pk); + let g_arr = hasher.finalize(); + IotaAddress(g_arr.digest) + } +} + +impl fmt::Display for IotaAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", Hex::encode(self.0)) + } +} + +impl fmt::Debug for IotaAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "0x{}", Hex::encode(self.0)) + } +} + +impl fmt::Display for MoveObjectType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + let s: StructTag = self.clone().into(); + write!( + f, + "{}", + to_iota_struct_tag_string(&s).map_err(fmt::Error::custom)? + ) + } +} + +impl fmt::Display for ObjectType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ObjectType::Package => write!(f, "{}", PACKAGE), + ObjectType::Struct(t) => write!(f, "{}", t), + } + } +} + +impl From for AccountAddress { + fn from(obj_id: ObjectID) -> Self { + obj_id.0 + } +} + +impl From for AccountAddress { + fn from(address: IotaAddress) -> Self { + Self::new(address.0) + } +} + +pub const STD_OPTION_MODULE_NAME: &IdentStr = ident_str!("option"); +pub const STD_OPTION_STRUCT_NAME: &IdentStr = ident_str!("Option"); +pub const RESOLVED_STD_OPTION: (&AccountAddress, &IdentStr, &IdentStr) = ( + &MOVE_STDLIB_ADDRESS, + STD_OPTION_MODULE_NAME, + STD_OPTION_STRUCT_NAME, +); + +pub const STD_ASCII_MODULE_NAME: &IdentStr = ident_str!("ascii"); +pub const STD_ASCII_STRUCT_NAME: &IdentStr = ident_str!("String"); +pub const RESOLVED_ASCII_STR: (&AccountAddress, &IdentStr, &IdentStr) = ( + &MOVE_STDLIB_ADDRESS, + STD_ASCII_MODULE_NAME, + STD_ASCII_STRUCT_NAME, +); + +pub const STD_UTF8_MODULE_NAME: &IdentStr = ident_str!("string"); +pub const STD_UTF8_STRUCT_NAME: &IdentStr = ident_str!("String"); +pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = ( + &MOVE_STDLIB_ADDRESS, + STD_UTF8_MODULE_NAME, + STD_UTF8_STRUCT_NAME, +); \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/coin.rs b/identity_iota_interaction/src/sdk_types/iota_types/coin.rs new file mode 100644 index 0000000000..5bd2608b3c --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/coin.rs @@ -0,0 +1,186 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Deserialize; +use serde::Serialize; + +use crate::ident_str; + +use super::super::move_core_types::language_storage::{StructTag, TypeTag}; +use super::super::move_core_types::identifier::IdentStr; +use super::super::move_core_types::annotated_value::{MoveStructLayout, MoveFieldLayout, MoveTypeLayout}; + +use super::id::UID; +use super::IOTA_FRAMEWORK_ADDRESS; +use super::error::{IotaError, ExecutionError, ExecutionErrorKind}; +use super::balance::{Supply, Balance}; +use super::base_types::ObjectID; + +pub const COIN_MODULE_NAME: &IdentStr = ident_str!("coin"); +pub const COIN_STRUCT_NAME: &IdentStr = ident_str!("Coin"); +pub const COIN_METADATA_STRUCT_NAME: &IdentStr = ident_str!("CoinMetadata"); +pub const COIN_TREASURE_CAP_NAME: &IdentStr = ident_str!("TreasuryCap"); + +pub const PAY_MODULE_NAME: &IdentStr = ident_str!("pay"); +pub const PAY_JOIN_FUNC_NAME: &IdentStr = ident_str!("join"); +pub const PAY_SPLIT_N_FUNC_NAME: &IdentStr = ident_str!("divide_and_keep"); +pub const PAY_SPLIT_VEC_FUNC_NAME: &IdentStr = ident_str!("split_vec"); + +// Rust version of the Move iota::coin::Coin type +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct Coin { + pub id: UID, + pub balance: Balance, +} + +impl Coin { + pub fn new(id: UID, value: u64) -> Self { + Self { + id, + balance: Balance::new(value), + } + } + + pub fn type_(type_param: TypeTag) -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + name: COIN_STRUCT_NAME.to_owned(), + module: COIN_MODULE_NAME.to_owned(), + type_params: vec![type_param], + } + } + + /// Is this other StructTag representing a Coin? + pub fn is_coin(other: &StructTag) -> bool { + other.address == IOTA_FRAMEWORK_ADDRESS + && other.module.as_ident_str() == COIN_MODULE_NAME + && other.name.as_ident_str() == COIN_STRUCT_NAME + } + + /// Create a coin from BCS bytes + pub fn from_bcs_bytes(content: &[u8]) -> Result { + bcs::from_bytes(content) + } + + pub fn id(&self) -> &ObjectID { + self.id.object_id() + } + + pub fn value(&self) -> u64 { + self.balance.value() + } + + pub fn to_bcs_bytes(&self) -> Vec { + bcs::to_bytes(&self).unwrap() + } + + pub fn layout(type_param: TypeTag) -> MoveStructLayout { + MoveStructLayout { + type_: Self::type_(type_param.clone()), + fields: vec![ + MoveFieldLayout::new( + ident_str!("id").to_owned(), + MoveTypeLayout::Struct(UID::layout()), + ), + MoveFieldLayout::new( + ident_str!("balance").to_owned(), + MoveTypeLayout::Struct(Balance::layout(type_param)), + ), + ], + } + } + + /// Add balance to this coin, erroring if the new total balance exceeds the + /// maximum + pub fn add(&mut self, balance: Balance) -> Result<(), ExecutionError> { + let Some(new_value) = self.value().checked_add(balance.value()) else { + return Err(ExecutionError::from_kind( + ExecutionErrorKind::CoinBalanceOverflow, + )); + }; + self.balance = Balance::new(new_value); + Ok(()) + } + + // Split amount out of this coin to a new coin. + // Related coin objects need to be updated in temporary_store to persist the + // changes, including creating the coin object related to the newly created + // coin. + pub fn split(&mut self, amount: u64, new_coin_id: UID) -> Result { + self.balance.withdraw(amount)?; + Ok(Coin::new(new_coin_id, amount)) + } +} + +// Rust version of the Move iota::coin::TreasuryCap type +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct TreasuryCap { + pub id: UID, + pub total_supply: Supply, +} + +impl TreasuryCap { + pub fn is_treasury_type(other: &StructTag) -> bool { + other.address == IOTA_FRAMEWORK_ADDRESS + && other.module.as_ident_str() == COIN_MODULE_NAME + && other.name.as_ident_str() == COIN_TREASURE_CAP_NAME + } + + /// Create a TreasuryCap from BCS bytes + pub fn from_bcs_bytes(content: &[u8]) -> Result { + bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization { + error: format!("Unable to deserialize TreasuryCap object: {}", err), + }) + } + + pub fn type_(type_param: StructTag) -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + name: COIN_TREASURE_CAP_NAME.to_owned(), + module: COIN_MODULE_NAME.to_owned(), + type_params: vec![TypeTag::Struct(Box::new(type_param))], + } + } +} + +// Rust version of the Move iota::coin::CoinMetadata type +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct CoinMetadata { + pub id: UID, + /// Number of decimal places the coin uses. + pub decimals: u8, + /// Name for the token + pub name: String, + /// Symbol for the token + pub symbol: String, + /// Description of the token + pub description: String, + /// URL for the token logo + pub icon_url: Option, +} + +impl CoinMetadata { + /// Is this other StructTag representing a CoinMetadata? + pub fn is_coin_metadata(other: &StructTag) -> bool { + other.address == IOTA_FRAMEWORK_ADDRESS + && other.module.as_ident_str() == COIN_MODULE_NAME + && other.name.as_ident_str() == COIN_METADATA_STRUCT_NAME + } + + /// Create a coin from BCS bytes + pub fn from_bcs_bytes(content: &[u8]) -> Result { + bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization { + error: format!("Unable to deserialize CoinMetadata object: {}", err), + }) + } + + pub fn type_(type_param: StructTag) -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + name: COIN_METADATA_STRUCT_NAME.to_owned(), + module: COIN_MODULE_NAME.to_owned(), + type_params: vec![TypeTag::Struct(Box::new(type_param))], + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/collection_types.rs b/identity_iota_interaction/src/sdk_types/iota_types/collection_types.rs new file mode 100644 index 0000000000..28d851db4a --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/collection_types.rs @@ -0,0 +1,103 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; + +use super::{base_types::ObjectID, id::UID}; + +/// Rust version of the Move iota::vec_map::VecMap type +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct VecMap { + pub contents: Vec>, +} + +/// Rust version of the Move iota::vec_map::Entry type +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct Entry { + pub key: K, + pub value: V, +} + +/// Rust version of the Move iota::vec_set::VecSet type +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct VecSet { + pub contents: Vec, +} + +/// Rust version of the Move iota::table::Table type. +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct TableVec { + pub contents: Table, +} + +impl Default for TableVec { + fn default() -> Self { + TableVec { + contents: Table { + id: ObjectID::ZERO, + size: 0, + }, + } + } +} + +/// Rust version of the Move iota::table::Table type. +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct Table { + pub id: ObjectID, + pub size: u64, +} + +impl Default for Table { + fn default() -> Self { + Table { + id: ObjectID::ZERO, + size: 0, + } + } +} + +/// Rust version of the Move iota::linked_table::LinkedTable type. +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct LinkedTable { + pub id: ObjectID, + pub size: u64, + pub head: Option, + pub tail: Option, +} + +impl Default for LinkedTable { + fn default() -> Self { + LinkedTable { + id: ObjectID::ZERO, + size: 0, + head: None, + tail: None, + } + } +} + +/// Rust version of the Move iota::linked_table::Node type. +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct LinkedTableNode { + pub prev: Option, + pub next: Option, + pub value: V, +} + +/// Rust version of the Move iota::bag::Bag type. +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct Bag { + pub id: UID, + pub size: u64, +} + +impl Default for Bag { + fn default() -> Self { + Self { + id: UID::new(ObjectID::ZERO), + size: 0, + } + } +} diff --git a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs new file mode 100644 index 0000000000..4087ed8df7 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs @@ -0,0 +1,207 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use strum::EnumString; + +use fastcrypto::{ + bls12381::min_sig::{ + BLS12381AggregateSignature, BLS12381AggregateSignatureAsBytes, BLS12381KeyPair, + BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature, + }, + ed25519::{ + Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes, + Ed25519Signature + }, + hash::{Blake2b256, HashFunction}, + traits::{VerifyingKey, Authenticator}, + encoding::{Base64}, + secp256k1::{ + Secp256k1PublicKeyAsBytes + }, + secp256r1::{ + Secp256r1PublicKeyAsBytes + } +}; +use fastcrypto_zkp::{zk_login_utils::Bn254FrElement}; + +use serde::Deserialize; +use serde::Serialize; +use serde_with::{serde_as, Bytes}; + +use super::{ + iota_serde::{Readable}, + error::{IotaResult, IotaError}, +}; + +// Authority Objects +pub type AuthorityKeyPair = BLS12381KeyPair; +pub type AuthorityPublicKey = BLS12381PublicKey; +pub type AuthorityPrivateKey = BLS12381PrivateKey; +pub type AuthoritySignature = BLS12381Signature; +pub type AggregateAuthoritySignature = BLS12381AggregateSignature; +pub type AggregateAuthoritySignatureAsBytes = BLS12381AggregateSignatureAsBytes; + +// TODO(joyqvq): prefix these types with Default, DefaultAccountKeyPair etc +pub type AccountKeyPair = Ed25519KeyPair; +pub type AccountPublicKey = Ed25519PublicKey; +pub type AccountPrivateKey = Ed25519PrivateKey; + +pub type NetworkKeyPair = Ed25519KeyPair; +pub type NetworkPublicKey = Ed25519PublicKey; +pub type NetworkPrivateKey = Ed25519PrivateKey; + +pub type DefaultHash = Blake2b256; + + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PublicKey { + Ed25519(Ed25519PublicKeyAsBytes), + Secp256k1(Secp256k1PublicKeyAsBytes), + Secp256r1(Secp256r1PublicKeyAsBytes), + ZkLogin(ZkLoginPublicIdentifier), +} +/// A wrapper struct to retrofit in [enum PublicKey] for zkLogin. +/// Useful to construct [struct MultiSigPublicKey]. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ZkLoginPublicIdentifier(pub Vec); // #[schemars(with = "Base64")] + +impl ZkLoginPublicIdentifier { + /// Consists of iss_bytes_len || iss_bytes || padded_32_byte_address_seed. + pub fn new(iss: &str, address_seed: &Bn254FrElement) -> IotaResult { + let mut bytes = Vec::new(); + let iss_bytes = iss.as_bytes(); + bytes.extend([iss_bytes.len() as u8]); + bytes.extend(iss_bytes); + bytes.extend(address_seed.padded()); + + Ok(Self(bytes)) + } +} +impl AsRef<[u8]> for PublicKey { + fn as_ref(&self) -> &[u8] { + match self { + PublicKey::Ed25519(pk) => &pk.0, + PublicKey::Secp256k1(pk) => &pk.0, + PublicKey::Secp256r1(pk) => &pk.0, + PublicKey::ZkLogin(z) => &z.0, + } + } +} + +impl PublicKey { + pub fn scheme(&self) -> SignatureScheme { + match self { + PublicKey::Ed25519(_) => SignatureScheme::ED25519, + PublicKey::Secp256k1(_) => SignatureScheme::Secp256k1, + PublicKey::Secp256r1(_) => SignatureScheme::Secp256r1, + PublicKey::ZkLogin(_) => SignatureScheme::ZkLoginAuthenticator, + } + } +} + +/// Defines the compressed version of the public key that we pass around +/// in Iota +#[serde_as] +#[derive( +Copy, +Clone, +PartialEq, +Eq, +Hash, +PartialOrd, +Ord, +Serialize, +Deserialize, +Debug +)] +pub struct AuthorityPublicKeyBytes( + #[serde_as(as = "Readable")] + pub [u8; AuthorityPublicKey::LENGTH], +); + +// BLS Port +// + +impl IotaPublicKey for BLS12381PublicKey { + const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::BLS12381; +} + +// Ed25519 Iota Signature port +// + +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct Ed25519IotaSignature( + #[serde_as(as = "Readable")] + [u8; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1], +); + +// Implementation useful for simplify testing when mock signature is needed +impl Default for Ed25519IotaSignature { + fn default() -> Self { + Self([0; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1]) + } +} + +impl IotaPublicKey for Ed25519PublicKey { + const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519; +} + +pub trait IotaPublicKey: VerifyingKey { + const SIGNATURE_SCHEME: SignatureScheme; +} + + +#[derive(Clone, Copy, Deserialize, Serialize, Debug, EnumString, strum::Display)] +#[strum(serialize_all = "lowercase")] +pub enum SignatureScheme { + ED25519, + Secp256k1, + Secp256r1, + BLS12381, // This is currently not supported for user Iota Address. + MultiSig, + ZkLoginAuthenticator, +} + +impl SignatureScheme { + pub fn flag(&self) -> u8 { + match self { + SignatureScheme::ED25519 => 0x00, + SignatureScheme::Secp256k1 => 0x01, + SignatureScheme::Secp256r1 => 0x02, + SignatureScheme::MultiSig => 0x03, + SignatureScheme::BLS12381 => 0x04, // This is currently not supported for user Iota + // Address. + SignatureScheme::ZkLoginAuthenticator => 0x05, + } + } + + /// Takes as input an hasher and updates it with a flag byte if the input + /// scheme is not ED25519; it does nothing otherwise. + pub fn update_hasher_with_flag(&self, hasher: &mut DefaultHash) { + match self { + SignatureScheme::ED25519 => (), + _ => hasher.update([self.flag()]), + }; + } + + pub fn from_flag(flag: &str) -> Result { + let byte_int = flag + .parse::() + .map_err(|_| IotaError::KeyConversion("Invalid key scheme".to_string()))?; + Self::from_flag_byte(&byte_int) + } + + pub fn from_flag_byte(byte_int: &u8) -> Result { + match byte_int { + 0x00 => Ok(SignatureScheme::ED25519), + 0x01 => Ok(SignatureScheme::Secp256k1), + 0x02 => Ok(SignatureScheme::Secp256r1), + 0x03 => Ok(SignatureScheme::MultiSig), + 0x04 => Ok(SignatureScheme::BLS12381), + 0x05 => Ok(SignatureScheme::ZkLoginAuthenticator), + _ => Err(IotaError::KeyConversion("Invalid key scheme".to_string())), + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/digests.rs b/identity_iota_interaction/src/sdk_types/iota_types/digests.rs new file mode 100644 index 0000000000..0c2058f517 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/digests.rs @@ -0,0 +1,314 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt; + +use serde::Deserialize; +use serde::Serialize; +use serde_with::{serde_as, Bytes}; + +use fastcrypto::encoding::{Base58, Encoding}; + +use super::iota_serde::Readable; + +/// A representation of a 32 byte digest +#[serde_as] +#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct Digest( + #[serde_as(as = "Readable")] + [u8; 32], +); + +impl Digest { + pub const ZERO: Self = Digest([0; 32]); + + pub const fn new(digest: [u8; 32]) -> Self { + Self(digest) + } + + pub fn generate(mut rng: R) -> Self { + let mut bytes = [0; 32]; + rng.fill_bytes(&mut bytes); + Self(bytes) + } + + pub fn random() -> Self { + Self::generate(rand::thread_rng()) + } + + pub const fn inner(&self) -> &[u8; 32] { + &self.0 + } + + pub const fn into_inner(self) -> [u8; 32] { + self.0 + } + + pub fn next_lexicographical(&self) -> Option { + let mut next_digest = *self; + let pos = next_digest.0.iter().rposition(|&byte| byte != 255)?; + next_digest.0[pos] += 1; + next_digest + .0 + .iter_mut() + .skip(pos + 1) + .for_each(|byte| *byte = 0); + Some(next_digest) + } +} + +impl fmt::Display for Digest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // TODO avoid the allocation + f.write_str(&Base58::encode(self.0)) + } +} + +impl fmt::Debug for Digest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::LowerHex for Digest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + + for byte in self.0 { + write!(f, "{:02x}", byte)?; + } + + Ok(()) + } +} + +impl fmt::UpperHex for Digest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + + for byte in self.0 { + write!(f, "{:02X}", byte)?; + } + + Ok(()) + } +} + +// Each object has a unique digest +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct ObjectDigest(Digest); + +impl fmt::Display for ObjectDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl fmt::Debug for ObjectDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "o#{}", self.0) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct TransactionDigest(Digest); + +impl Default for TransactionDigest { + fn default() -> Self { + Self::ZERO + } +} + +impl TransactionDigest { + pub const ZERO: Self = Self(Digest::ZERO); + + pub const fn new(digest: [u8; 32]) -> Self { + Self(Digest::new(digest)) + } + + /// A digest we use to signify the parent transaction was the genesis, + /// ie. for an object there is no parent digest. + /// Note that this is not the same as the digest of the genesis transaction, + /// which cannot be known ahead of time. + // TODO(https://github.com/iotaledger/iota/issues/65): we can pick anything here + pub const fn genesis_marker() -> Self { + Self::ZERO + } + + pub fn generate(rng: R) -> Self { + Self(Digest::generate(rng)) + } + + pub fn random() -> Self { + Self(Digest::random()) + } + + pub fn inner(&self) -> &[u8; 32] { + self.0.inner() + } + + pub fn into_inner(self) -> [u8; 32] { + self.0.into_inner() + } + + pub fn base58_encode(&self) -> String { + Base58::encode(self.0) + } +} + +impl AsRef<[u8]> for Digest { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8; 32]> for Digest { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl fmt::Display for TransactionDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl fmt::Debug for TransactionDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("TransactionDigest").field(&self.0).finish() + } +} + +impl fmt::LowerHex for TransactionDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl fmt::UpperHex for TransactionDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl TryFrom<&[u8]> for TransactionDigest { + type Error = super::error::IotaError; + + fn try_from(bytes: &[u8]) -> Result { + let arr: [u8; 32] = bytes + .try_into() + .map_err(|_| super::error::IotaError::InvalidTransactionDigest)?; + Ok(Self::new(arr)) + } +} + +impl std::str::FromStr for TransactionDigest { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let mut result = [0; 32]; + result.copy_from_slice(&Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?); + Ok(TransactionDigest::new(result)) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct WrappedDigest(Digest); + +impl WrappedDigest { + pub const ZERO: Self = Self(Digest::ZERO); + + pub const fn new(digest: [u8; 32]) -> Self { + Self(Digest::new(digest)) + } + + pub fn generate(rng: R) -> Self { + Self(Digest::generate(rng)) + } + + pub fn random() -> Self { + Self(Digest::random()) + } + + pub const fn inner(&self) -> &[u8; 32] { + self.0.inner() + } + + pub const fn into_inner(self) -> [u8; 32] { + self.0.into_inner() + } + + pub fn base58_encode(&self) -> String { + Base58::encode(self.0) + } + + pub fn next_lexicographical(&self) -> Option { + self.0.next_lexicographical().map(Self) + } +} + +impl AsRef<[u8]> for WrappedDigest { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8; 32]> for WrappedDigest { + fn as_ref(&self) -> &[u8; 32] { + self.0.as_ref() + } +} + +impl From for [u8; 32] { + fn from(digest: WrappedDigest) -> Self { + digest.into_inner() + } +} + +impl From<[u8; 32]> for WrappedDigest { + fn from(digest: [u8; 32]) -> Self { + Self::new(digest) + } +} + +impl fmt::Display for WrappedDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl fmt::Debug for WrappedDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("WrappedDigest") + .field(&self.0) + .finish() + } +} + +impl std::str::FromStr for WrappedDigest { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let mut result = [0; 32]; + result.copy_from_slice(&Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?); + Ok(WrappedDigest::new(result)) + } +} + +impl fmt::LowerHex for WrappedDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl fmt::UpperHex for WrappedDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/dynamic_field.rs b/identity_iota_interaction/src/sdk_types/iota_types/dynamic_field.rs new file mode 100644 index 0000000000..f8827f1b4e --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/dynamic_field.rs @@ -0,0 +1,156 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + fmt, + fmt::{Display, Formatter}, +}; + +use fastcrypto::{encoding::Base58}; + +use crate::ident_str; + +use super::super::move_core_types::{ + // annotated_value::{MoveStruct, MoveValue}, + identifier::IdentStr, + language_storage::{StructTag, TypeTag}, +}; + +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use serde_with::{serde_as, DisplayFromStr}; + +use super::{ + base_types::{ObjectID, SequenceNumber}, + digests::{ObjectDigest}, + error::{IotaError, IotaResult}, + id::UID, + iota_serde::{IotaTypeTag, Readable}, + //object::Object, + //storage::ObjectStore, + IOTA_FRAMEWORK_ADDRESS, +}; + +const DYNAMIC_FIELD_MODULE_NAME: &IdentStr = ident_str!("dynamic_field"); +const DYNAMIC_FIELD_FIELD_STRUCT_NAME: &IdentStr = ident_str!("Field"); + +const DYNAMIC_OBJECT_FIELD_MODULE_NAME: &IdentStr = ident_str!("dynamic_object_field"); +const DYNAMIC_OBJECT_FIELD_WRAPPER_STRUCT_NAME: &IdentStr = ident_str!("Wrapper"); + +/// Rust version of the Move iota::dynamic_field::Field type +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Field { + pub id: UID, + pub name: N, + pub value: V, +} + +#[serde_as] +#[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct DynamicFieldInfo { + pub name: DynamicFieldName, + #[serde_as(as = "Readable")] + pub bcs_name: Vec, + pub type_: DynamicFieldType, + pub object_type: String, + pub object_id: ObjectID, + pub version: SequenceNumber, + pub digest: ObjectDigest, +} + +#[serde_as] +#[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct DynamicFieldName { + #[serde_as(as = "Readable")] + pub type_: TypeTag, + // Bincode does not like serde_json::Value, rocksdb will not insert the value without + // serializing value as string. TODO: investigate if this can be removed after switch to + // BCS. + #[serde_as(as = "Readable<_, DisplayFromStr>")] + pub value: Value, +} + +impl Display for DynamicFieldName { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.type_, self.value) + } +} + +#[derive(Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Debug)] +pub enum DynamicFieldType { + #[serde(rename_all = "camelCase")] + DynamicField, + DynamicObject, +} + +impl Display for DynamicFieldType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DynamicFieldType::DynamicField => write!(f, "DynamicField"), + DynamicFieldType::DynamicObject => write!(f, "DynamicObject"), + } + } +} + +impl DynamicFieldInfo { + pub fn is_dynamic_field(tag: &StructTag) -> bool { + tag.address == IOTA_FRAMEWORK_ADDRESS + && tag.module.as_ident_str() == DYNAMIC_FIELD_MODULE_NAME + && tag.name.as_ident_str() == DYNAMIC_FIELD_FIELD_STRUCT_NAME + } + + pub fn is_dynamic_object_field_wrapper(tag: &StructTag) -> bool { + tag.address == IOTA_FRAMEWORK_ADDRESS + && tag.module.as_ident_str() == DYNAMIC_OBJECT_FIELD_MODULE_NAME + && tag.name.as_ident_str() == DYNAMIC_OBJECT_FIELD_WRAPPER_STRUCT_NAME + } + + pub fn dynamic_field_type(key: TypeTag, value: TypeTag) -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + name: DYNAMIC_FIELD_FIELD_STRUCT_NAME.to_owned(), + module: DYNAMIC_FIELD_MODULE_NAME.to_owned(), + type_params: vec![key, value], + } + } + + pub fn dynamic_object_field_wrapper(key: TypeTag) -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + module: DYNAMIC_OBJECT_FIELD_MODULE_NAME.to_owned(), + name: DYNAMIC_OBJECT_FIELD_WRAPPER_STRUCT_NAME.to_owned(), + type_params: vec![key], + } + } + + pub fn try_extract_field_name( + tag: &StructTag, + type_: &DynamicFieldType, + ) -> IotaResult { + match (type_, tag.type_params.first()) { + (DynamicFieldType::DynamicField, Some(name_type)) => Ok(name_type.clone()), + (DynamicFieldType::DynamicObject, Some(TypeTag::Struct(s))) => Ok(s + .type_params + .first() + .ok_or_else(|| IotaError::ObjectDeserialization { + error: format!("Error extracting dynamic object name from object: {tag}"), + })? + .clone()), + _ => Err(IotaError::ObjectDeserialization { + error: format!("Error extracting dynamic object name from object: {tag}"), + }), + } + } + + pub fn try_extract_field_value(tag: &StructTag) -> IotaResult { + match tag.type_params.last() { + Some(value_type) => Ok(value_type.clone()), + None => Err(IotaError::ObjectDeserialization { + error: format!("Error extracting dynamic object value from object: {tag}"), + }), + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/error.rs b/identity_iota_interaction/src/sdk_types/iota_types/error.rs new file mode 100644 index 0000000000..42a6565393 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/error.rs @@ -0,0 +1,877 @@ +// Copyright (c) 2021, Facebook, Inc. and its affiliates +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{collections::BTreeMap, fmt::Debug}; + +use serde::{Deserialize, Serialize}; +use strum::{AsRefStr, IntoStaticStr}; +use thiserror::Error; + +use super::super::{ + rpc_types::CheckpointSequenceNumber, +}; +use super::{ + base_types::*, + //committee::{Committee, EpochId, StakeUnit}, + digests::{ObjectDigest, TransactionDigest, WrappedDigest}, + execution_status::{CommandArgumentError, ExecutionFailureStatus}, + //messages_checkpoint::CheckpointSequenceNumber, + object::Owner, +}; + +pub const TRANSACTION_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transaction"; +pub const TRANSACTIONS_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transactions"; + +#[macro_export] +macro_rules! fp_bail { + ($e:expr) => { + return Err($e) + }; +} + +#[macro_export(local_inner_macros)] +macro_rules! fp_ensure { + ($cond:expr, $e:expr) => { + if !($cond) { + fp_bail!($e); + } + }; +} + +#[macro_export] +macro_rules! exit_main { + ($result:expr) => { + match $result { + Ok(_) => (), + Err(err) => { + let err = format!("{:?}", err); + println!("{}", err.bold().red()); + std::process::exit(1); + } + } + }; +} + +#[macro_export] +macro_rules! make_invariant_violation { + ($($args:expr),* $(,)?) => {{ + if cfg!(debug_assertions) { + panic!($($args),*) + } + ExecutionError::invariant_violation(format!($($args),*)) + }} +} + +#[macro_export] +macro_rules! invariant_violation { + ($($args:expr),* $(,)?) => { + return Err(make_invariant_violation!($($args),*).into()) + }; +} + +#[macro_export] +macro_rules! assert_invariant { + ($cond:expr, $($args:expr),* $(,)?) => {{ + if !$cond { + invariant_violation!($($args),*) + } + }}; +} + +#[derive( + Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr, +)] +pub enum UserInputError { + #[error("Mutable object {object_id} cannot appear more than one in one transaction.")] + MutableObjectUsedMoreThanOnce { object_id: ObjectID }, + #[error("Wrong number of parameters for the transaction.")] + ObjectInputArityViolation, + #[error( + "Could not find the referenced object {:?} at version {:?}.", + object_id, + version + )] + ObjectNotFound { + object_id: ObjectID, + version: Option, + }, + #[error( + "Object {provided_obj_ref:?} is not available for consumption, its current version: {current_version:?}." + )] + ObjectVersionUnavailableForConsumption { + provided_obj_ref: ObjectRef, + current_version: SequenceNumber, + }, + #[error("Package verification failed: {err:?}")] + PackageVerificationTimedout { err: String }, + #[error("Dependent package not found on-chain: {package_id:?}")] + DependentPackageNotFound { package_id: ObjectID }, + #[error("Mutable parameter provided, immutable parameter expected.")] + ImmutableParameterExpectedError { object_id: ObjectID }, + #[error("Size limit exceeded: {limit} is {value}")] + SizeLimitExceeded { limit: String, value: String }, + #[error( + "Object {child_id:?} is owned by object {parent_id:?}. \ + Objects owned by other objects cannot be used as input arguments." + )] + InvalidChildObjectArgument { + child_id: ObjectID, + parent_id: ObjectID, + }, + #[error( + "Invalid Object digest for object {object_id:?}. Expected digest : {expected_digest:?}." + )] + InvalidObjectDigest { + object_id: ObjectID, + expected_digest: ObjectDigest, + }, + #[error("Sequence numbers above the maximal value are not usable for transfers.")] + InvalidSequenceNumber, + #[error("A move object is expected, instead a move package is passed: {object_id}")] + MovePackageAsObject { object_id: ObjectID }, + #[error("A move package is expected, instead a move object is passed: {object_id}")] + MoveObjectAsPackage { object_id: ObjectID }, + #[error("Transaction was not signed by the correct sender: {}", error)] + IncorrectUserSignature { error: String }, + + #[error("Object used as shared is not shared.")] + NotSharedObjectError, + #[error("The transaction inputs contain duplicated ObjectRef's")] + DuplicateObjectRefInput, + + // Gas related errors + #[error("Transaction gas payment missing.")] + MissingGasPayment, + #[error("Gas object is not an owned object with owner: {:?}.", owner)] + GasObjectNotOwnedObject { owner: Owner }, + #[error("Gas budget: {:?} is higher than max: {:?}.", gas_budget, max_budget)] + GasBudgetTooHigh { gas_budget: u64, max_budget: u64 }, + #[error("Gas budget: {:?} is lower than min: {:?}.", gas_budget, min_budget)] + GasBudgetTooLow { gas_budget: u64, min_budget: u64 }, + #[error( + "Balance of gas object {:?} is lower than the needed amount: {:?}.", + gas_balance, + needed_gas_amount + )] + GasBalanceTooLow { + gas_balance: u128, + needed_gas_amount: u128, + }, + #[error("Transaction kind does not support Sponsored Transaction")] + UnsupportedSponsoredTransactionKind, + #[error( + "Gas price {:?} under reference gas price (RGP) {:?}", + gas_price, + reference_gas_price + )] + GasPriceUnderRGP { + gas_price: u64, + reference_gas_price: u64, + }, + #[error("Gas price cannot exceed {:?} nanos", max_gas_price)] + GasPriceTooHigh { max_gas_price: u64 }, + #[error("Object {object_id} is not a gas object")] + InvalidGasObject { object_id: ObjectID }, + #[error("Gas object does not have enough balance to cover minimal gas spend")] + InsufficientBalanceToCoverMinimalGas, + + #[error( + "Could not find the referenced object {:?} as the asked version {:?} is higher than the latest {:?}", + object_id, + asked_version, + latest_version + )] + ObjectSequenceNumberTooHigh { + object_id: ObjectID, + asked_version: SequenceNumber, + latest_version: SequenceNumber, + }, + #[error("Object deleted at reference {:?}.", object_ref)] + ObjectDeleted { object_ref: ObjectRef }, + #[error("Invalid Batch Transaction: {}", error)] + InvalidBatchTransaction { error: String }, + #[error("This Move function is currently disabled and not available for call")] + BlockedMoveFunction, + #[error("Empty input coins for Pay related transaction")] + EmptyInputCoins, + + #[error( + "IOTA payment transactions use first input coin for gas payment, but found a different gas object." + )] + UnexpectedGasPaymentObject, + + #[error("Wrong initial version given for shared object")] + SharedObjectStartingVersionMismatch, + + #[error( + "Attempt to transfer object {object_id} that does not have public transfer. Object transfer must be done instead using a distinct Move function call." + )] + TransferObjectWithoutPublicTransferError { object_id: ObjectID }, + + #[error( + "TransferObjects, MergeCoin, and Publish cannot have empty arguments. \ + If MakeMoveVec has empty arguments, it must have a type specified" + )] + EmptyCommandInput, + + #[error("Transaction is denied: {}", error)] + TransactionDenied { error: String }, + + #[error("Feature is not supported: {0}")] + Unsupported(String), + + #[error("Query transactions with move function input error: {0}")] + MoveFunctionInputError(String), + + #[error("Verified checkpoint not found for sequence number: {0}")] + VerifiedCheckpointNotFound(CheckpointSequenceNumber), + + #[error("Verified checkpoint not found for digest: {0}")] + VerifiedCheckpointDigestNotFound(String), + + #[error("Latest checkpoint sequence number not found")] + LatestCheckpointSequenceNumberNotFound, + + #[error("Checkpoint contents not found for digest: {0}")] + CheckpointContentsNotFound(WrappedDigest), + + #[error("Genesis transaction not found")] + GenesisTransactionNotFound, + + #[error("Transaction {0} not found")] + TransactionCursorNotFound(u64), + + #[error( + "Object {:?} is a system object and cannot be accessed by user transactions.", + object_id + )] + InaccessibleSystemObject { object_id: ObjectID }, + #[error( + "{max_publish_commands} max publish/upgrade commands allowed, {publish_count} provided" + )] + MaxPublishCountExceeded { + max_publish_commands: u64, + publish_count: u64, + }, + + #[error("Immutable parameter provided, mutable parameter expected.")] + MutableParameterExpected { object_id: ObjectID }, + + #[error("Address {address:?} is denied for coin {coin_type}")] + AddressDeniedForCoin { + address: IotaAddress, + coin_type: String, + }, + + #[error("Commands following a command with Random can only be TransferObjects or MergeCoins")] + PostRandomCommandRestrictions, +} + +#[derive( + Eq, + PartialEq, + Clone, + Debug, + Serialize, + Deserialize, + Hash, + AsRefStr, + IntoStaticStr, + Error, +)] +#[serde(tag = "code", rename = "ObjectResponseError", rename_all = "camelCase")] +pub enum IotaObjectResponseError { + #[error("Object {:?} does not exist.", object_id)] + NotExists { object_id: ObjectID }, + #[error("Cannot find dynamic field for parent object {:?}.", parent_object_id)] + DynamicFieldNotFound { parent_object_id: ObjectID }, + #[error( + "Object has been deleted object_id: {:?} at version: {:?} in digest {:?}", + object_id, + version, + digest + )] + Deleted { + object_id: ObjectID, + /// Object version. + version: SequenceNumber, + /// Base64 string representing the object digest + digest: ObjectDigest, + }, + #[error("Unknown Error.")] + Unknown, + #[error("Display Error: {:?}", error)] + DisplayError { error: String }, + // TODO: also integrate IotaPastObjectResponse (VersionNotFound, VersionTooHigh) +} + +/// Custom error type for Iota. +#[derive( + Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr, +)] +pub enum IotaError { + #[error("Error checking transaction input objects: {:?}", error)] + UserInput { error: UserInputError }, + + #[error("Error checking transaction object: {:?}", error)] + IotaObjectResponse { error: IotaObjectResponseError }, + + #[error("Expecting a single owner, shared ownership found")] + UnexpectedOwnerType, + + #[error("There are already {queue_len} transactions pending, above threshold of {threshold}")] + TooManyTransactionsPendingExecution { queue_len: usize, threshold: usize }, + + #[error("There are too many transactions pending in consensus")] + TooManyTransactionsPendingConsensus, + + #[error( + "Input {object_id} already has {queue_len} transactions pending, above threshold of {threshold}" + )] + TooManyTransactionsPendingOnObject { + object_id: ObjectID, + queue_len: usize, + threshold: usize, + }, + + #[error( + "Input {object_id} has a transaction {txn_age_sec} seconds old pending, above threshold of {threshold} seconds" + )] + TooOldTransactionPendingOnObject { + object_id: ObjectID, + txn_age_sec: u64, + threshold: u64, + }, + + // Signature verification + #[error("Signature is not valid: {}", error)] + InvalidSignature { error: String }, + #[error("Required Signature from {expected} is absent {:?}.", actual)] + SignerSignatureAbsent { + expected: String, + actual: Vec, + }, + #[error("Expect {expected} signer signatures but got {actual}.")] + SignerSignatureNumberMismatch { expected: usize, actual: usize }, + #[error("Value was not signed by the correct sender: {}", error)] + IncorrectSigner { error: String }, + #[error( + "Value was not signed by a known authority. signer: {:?}, index: {:?}, committee: {committee}", + signer, + index + )] + UnknownSigner { + signer: Option, + index: Option, + committee: Box, + }, + #[error( + "Validator {:?} responded multiple signatures for the same message, conflicting: {:?}", + signer, + conflicting_sig + )] + StakeAggregatorRepeatedSigner { + signer: AuthorityName, + conflicting_sig: bool, + }, + // TODO: Used for distinguishing between different occurrences of invalid signatures, to allow + // retries in some cases. + #[error( + "Signature is not valid, but a retry may result in a valid one: {}", + error + )] + PotentiallyTemporarilyInvalidSignature { error: String }, + + // Certificate verification and execution + #[error( + "Signature or certificate from wrong epoch, expected {expected_epoch}, got {actual_epoch}" + )] + WrongEpoch { + expected_epoch: EpochId, + actual_epoch: EpochId, + }, + #[error("Signatures in a certificate must form a quorum")] + CertificateRequiresQuorum, + #[error("Transaction certificate processing failed: {err}")] + ErrorWhileProcessingCertificate { err: String }, + #[error( + "Failed to get a quorum of signed effects when processing transaction: {effects_map:?}" + )] + QuorumFailedToGetEffectsQuorumWhenProcessingTransaction { + effects_map: BTreeMap, StakeUnit)>, + }, + #[error( + "Failed to verify Tx certificate with executed effects, error: {error:?}, validator: {validator_name:?}" + )] + FailedToVerifyTxCertWithExecutedEffects { + validator_name: AuthorityName, + error: String, + }, + #[error("Transaction is already finalized but with different user signatures")] + TxAlreadyFinalizedWithDifferentUserSigs, + #[error("System Transaction not accepted")] + InvalidSystemTransaction, + + // Account access + #[error("Invalid authenticator")] + InvalidAuthenticator, + #[error("Invalid address")] + InvalidAddress, + #[error("Invalid transaction digest.")] + InvalidTransactionDigest, + + #[error("Invalid digest length. Expected {expected}, got {actual}")] + InvalidDigestLength { expected: usize, actual: usize }, + + #[error("Unexpected message.")] + UnexpectedMessage, + + // Move module publishing related errors + #[error("Failed to verify the Move module, reason: {error:?}.")] + ModuleVerificationFailure { error: String }, + #[error("Failed to deserialize the Move module, reason: {error:?}.")] + ModuleDeserializationFailure { error: String }, + #[error("Failed to publish the Move module(s), reason: {error}")] + ModulePublishFailure { error: String }, + #[error("Failed to build Move modules: {error}.")] + ModuleBuildFailure { error: String }, + + // Move call related errors + #[error("Function resolution failure: {error:?}.")] + FunctionNotFound { error: String }, + #[error("Module not found in package: {module_name:?}.")] + ModuleNotFound { module_name: String }, + #[error("Type error while binding function arguments: {error:?}.")] + Type { error: String }, + #[error("Circular object ownership detected")] + CircularObjectOwnership, + + // Internal state errors + #[error("Attempt to re-initialize a transaction lock for objects {:?}.", refs)] + ObjectLockAlreadyInitialized { refs: Vec }, + #[error( + "Object {obj_ref:?} already locked by a different transaction: {pending_transaction:?}" + )] + ObjectLockConflict { + obj_ref: ObjectRef, + pending_transaction: TransactionDigest, + }, + #[error( + "Objects {obj_refs:?} are already locked by a transaction from a future epoch {locked_epoch:?}), attempt to override with a transaction from epoch {new_epoch:?}" + )] + ObjectLockedAtFutureEpoch { + obj_refs: Vec, + locked_epoch: EpochId, + new_epoch: EpochId, + locked_by_tx: TransactionDigest, + }, + #[error("{TRANSACTION_NOT_FOUND_MSG_PREFIX} [{:?}].", digest)] + TransactionNotFound { digest: TransactionDigest }, + #[error("{TRANSACTIONS_NOT_FOUND_MSG_PREFIX} [{:?}].", digests)] + TransactionsNotFound { digests: Vec }, + #[error("Could not find the referenced transaction events [{digest:?}].")] + TransactionEventsNotFound { digest: WrappedDigest }, + #[error( + "Attempt to move to `Executed` state an transaction that has already been executed: {:?}.", + digest + )] + TransactionAlreadyExecuted { digest: TransactionDigest }, + #[error("Object ID did not have the expected type")] + BadObjectType { error: String }, + #[error("Fail to retrieve Object layout for {st}")] + FailObjectLayout { st: String }, + + #[error("Execution invariant violated")] + ExecutionInvariantViolation, + #[error("Validator {authority:?} is faulty in a Byzantine manner: {reason:?}")] + ByzantineAuthoritySuspicion { + authority: AuthorityName, + reason: String, + }, + #[error( + "Attempted to access {object} through parent {given_parent}, \ + but it's actual parent is {actual_owner}" + )] + InvalidChildObjectAccess { + object: ObjectID, + given_parent: ObjectID, + actual_owner: Owner, + }, + + #[error("Authority Error: {error:?}")] + GenericAuthority { error: String }, + + #[error("Failed to dispatch subscription: {error:?}")] + FailedToDispatchSubscription { error: String }, + + #[error("Failed to serialize Owner: {error:?}")] + OwnerFailedToSerialize { error: String }, + + #[error("Failed to deserialize fields into JSON: {error:?}")] + ExtraFieldFailedToDeserialize { error: String }, + + #[error("Failed to execute transaction locally by Orchestrator: {error:?}")] + TransactionOrchestratorLocalExecution { error: String }, + + // Errors returned by authority and client read API's + #[error("Failure serializing transaction in the requested format: {:?}", error)] + TransactionSerialization { error: String }, + #[error("Failure serializing object in the requested format: {:?}", error)] + ObjectSerialization { error: String }, + #[error("Failure deserializing object in the requested format: {:?}", error)] + ObjectDeserialization { error: String }, + #[error("Event store component is not active on this node")] + NoEventStore, + + // Client side error + #[error("Too many authority errors were detected for {}: {:?}", action, errors)] + TooManyIncorrectAuthorities { + errors: Vec<(AuthorityName, IotaError)>, + action: String, + }, + #[error("Invalid transaction range query to the fullnode: {:?}", error)] + FullNodeInvalidTxRangeQuery { error: String }, + + // Errors related to the authority-consensus interface. + #[error("Failed to submit transaction to consensus: {0}")] + FailedToSubmitToConsensus(String), + #[error("Failed to connect with consensus node: {0}")] + ConsensusConnectionBroken(String), + #[error("Failed to execute handle_consensus_transaction on Iota: {0}")] + HandleConsensusTransactionFailure(String), + + // Cryptography errors. + #[error("Signature key generation error: {0}")] + SignatureKeyGen(String), + #[error("Key Conversion Error: {0}")] + KeyConversion(String), + #[error("Invalid Private Key provided")] + InvalidPrivateKey, + + // Unsupported Operations on Fullnode + #[error("Fullnode does not support handle_certificate")] + FullNodeCantHandleCertificate, + + // Epoch related errors. + #[error("Validator temporarily stopped processing transactions due to epoch change")] + ValidatorHaltedAtEpochEnd, + #[error("Validator has stopped operations for this epoch")] + EpochEnded, + #[error("Error when advancing epoch: {:?}", error)] + AdvanceEpoch { error: String }, + + #[error("Transaction Expired")] + TransactionExpired, + + // These are errors that occur when an RPC fails and is simply the utf8 message sent in a + // Tonic::Status + #[error("{1} - {0}")] + Rpc(String, String), + + #[error("Use of disabled feature: {:?}", error)] + UnsupportedFeature { error: String }, + + #[error("Unable to communicate with the Quorum Driver channel: {:?}", error)] + QuorumDriverCommunication { error: String }, + + #[error("Operation timed out")] + Timeout, + + #[error("Error executing {0}")] + Execution(String), + + #[error("Invalid committee composition")] + InvalidCommittee(String), + + #[error("Missing committee information for epoch {0}")] + MissingCommitteeAtEpoch(EpochId), + + #[error("Index store not available on this Fullnode.")] + IndexStoreNotAvailable, + + #[error("Failed to read dynamic field from table in the object store: {0}")] + DynamicFieldRead(String), + + #[error("Failed to read or deserialize system state related data structures on-chain: {0}")] + IotaSystemStateRead(String), + + #[error("Unexpected version error: {0}")] + UnexpectedVersion(String), + + #[error("Message version is not supported at the current protocol version: {error}")] + WrongMessageVersion { error: String }, + + #[error("unknown error: {0}")] + Unknown(String), + + #[error("Failed to perform file operation: {0}")] + FileIO(String), + + #[error("Failed to get JWK")] + JWKRetrieval, + + #[error("Storage error: {0}")] + Storage(String), + + #[error( + "Validator cannot handle the request at the moment. Please retry after at least {retry_after_secs} seconds." + )] + ValidatorOverloadedRetryAfter { retry_after_secs: u64 }, +} + +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +/// Sub-status codes for the `UNKNOWN_VERIFICATION_ERROR` VM Status Code which +/// provides more context TODO: add more Vm Status errors. We use +/// `UNKNOWN_VERIFICATION_ERROR` as a catchall for now. +pub enum VMMVerifierErrorSubStatusCode { + MULTIPLE_RETURN_VALUES_NOT_ALLOWED = 0, + INVALID_OBJECT_CREATION = 1, +} + +#[repr(u64)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +/// Sub-status codes for the `MEMORY_LIMIT_EXCEEDED` VM Status Code which +/// provides more context +pub enum VMMemoryLimitExceededSubStatusCode { + EVENT_COUNT_LIMIT_EXCEEDED = 0, + EVENT_SIZE_LIMIT_EXCEEDED = 1, + NEW_ID_COUNT_LIMIT_EXCEEDED = 2, + DELETED_ID_COUNT_LIMIT_EXCEEDED = 3, + TRANSFER_ID_COUNT_LIMIT_EXCEEDED = 4, + OBJECT_RUNTIME_CACHE_LIMIT_EXCEEDED = 5, + OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED = 6, + TOTAL_EVENT_SIZE_LIMIT_EXCEEDED = 7, +} + +pub type IotaResult = Result; +pub type UserInputResult = Result; + +impl From for IotaError { + fn from(error: ExecutionError) -> Self { + IotaError::Execution(error.to_string()) + } +} + +impl From for IotaError { + fn from(kind: ExecutionErrorKind) -> Self { + ExecutionError::from_kind(kind).into() + } +} + +impl From<&str> for IotaError { + fn from(error: &str) -> Self { + IotaError::GenericAuthority { + error: error.to_string(), + } + } +} + +impl TryFrom for UserInputError { + type Error = anyhow::Error; + + fn try_from(err: IotaError) -> Result { + match err { + IotaError::UserInput { error } => Ok(error), + other => anyhow::bail!("error {:?} is not UserInput", other), + } + } +} + +impl From for IotaError { + fn from(error: UserInputError) -> Self { + IotaError::UserInput { error } + } +} + +impl From for IotaError { + fn from(error: IotaObjectResponseError) -> Self { + IotaError::IotaObjectResponse { error } + } +} + +impl IotaError { + pub fn individual_error_indicates_epoch_change(&self) -> bool { + matches!( + self, + IotaError::ValidatorHaltedAtEpochEnd | IotaError::MissingCommitteeAtEpoch(_) + ) + } + + /// Returns if the error is retryable and if the error's retryability is + /// explicitly categorized. + /// There should be only a handful of retryable errors. For now we list + /// common non-retryable error below to help us find more retryable + /// errors in logs. + pub fn is_retryable(&self) -> (bool, bool) { + let retryable = match self { + // Network error + IotaError::Rpc { .. } => true, + + // Reconfig error + IotaError::ValidatorHaltedAtEpochEnd => true, + IotaError::MissingCommitteeAtEpoch(..) => true, + IotaError::WrongEpoch { .. } => true, + + IotaError::UserInput { error } => { + match error { + // Only ObjectNotFound and DependentPackageNotFound is potentially retryable + UserInputError::ObjectNotFound { .. } => true, + UserInputError::DependentPackageNotFound { .. } => true, + _ => false, + } + } + + IotaError::PotentiallyTemporarilyInvalidSignature { .. } => true, + + // Overload errors + IotaError::TooManyTransactionsPendingExecution { .. } => true, + IotaError::TooManyTransactionsPendingOnObject { .. } => true, + IotaError::TooOldTransactionPendingOnObject { .. } => true, + IotaError::TooManyTransactionsPendingConsensus => true, + IotaError::ValidatorOverloadedRetryAfter { .. } => true, + + // Non retryable error + IotaError::Execution(..) => false, + IotaError::ByzantineAuthoritySuspicion { .. } => false, + IotaError::QuorumFailedToGetEffectsQuorumWhenProcessingTransaction { .. } => false, + IotaError::TxAlreadyFinalizedWithDifferentUserSigs => false, + IotaError::FailedToVerifyTxCertWithExecutedEffects { .. } => false, + IotaError::ObjectLockConflict { .. } => false, + + // For all un-categorized errors, return here with categorized = false. + _ => return (false, false), + }; + + (retryable, true) + } + + pub fn is_object_or_package_not_found(&self) -> bool { + match self { + IotaError::UserInput { error } => { + matches!( + error, + UserInputError::ObjectNotFound { .. } + | UserInputError::DependentPackageNotFound { .. } + ) + } + _ => false, + } + } + + pub fn is_overload(&self) -> bool { + matches!( + self, + IotaError::TooManyTransactionsPendingExecution { .. } + | IotaError::TooManyTransactionsPendingOnObject { .. } + | IotaError::TooOldTransactionPendingOnObject { .. } + | IotaError::TooManyTransactionsPendingConsensus + ) + } + + pub fn is_retryable_overload(&self) -> bool { + matches!(self, IotaError::ValidatorOverloadedRetryAfter { .. }) + } +} + +impl Ord for IotaError { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + Ord::cmp(self.as_ref(), other.as_ref()) + } +} + +impl PartialOrd for IotaError { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +type BoxError = Box; + +pub type ExecutionErrorKind = ExecutionFailureStatus; + +#[derive(Debug)] +pub struct ExecutionError { + inner: Box, +} + +#[derive(Debug)] +struct ExecutionErrorInner { + kind: ExecutionErrorKind, + source: Option, + command: Option, +} + +impl ExecutionError { + pub fn new(kind: ExecutionErrorKind, source: Option) -> Self { + Self { + inner: Box::new(ExecutionErrorInner { + kind, + source, + command: None, + }), + } + } + + pub fn new_with_source>(kind: ExecutionErrorKind, source: E) -> Self { + Self::new(kind, Some(source.into())) + } + + pub fn invariant_violation>(source: E) -> Self { + Self::new_with_source(ExecutionFailureStatus::InvariantViolation, source) + } + + pub fn with_command_index(mut self, command: CommandIndex) -> Self { + self.inner.command = Some(command); + self + } + + pub fn from_kind(kind: ExecutionErrorKind) -> Self { + Self::new(kind, None) + } + + pub fn kind(&self) -> &ExecutionErrorKind { + &self.inner.kind + } + + pub fn command(&self) -> Option { + self.inner.command + } + + pub fn source(&self) -> &Option { + &self.inner.source + } + + pub fn to_execution_status(&self) -> (ExecutionFailureStatus, Option) { + (self.kind().clone(), self.command()) + } +} + +impl std::fmt::Display for ExecutionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ExecutionError: {:?}", self) + } +} + +impl std::error::Error for ExecutionError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.inner.source.as_ref().map(|e| &**e as _) + } +} + +impl From for ExecutionError { + fn from(kind: ExecutionErrorKind) -> Self { + Self::from_kind(kind) + } +} + +pub fn command_argument_error(e: CommandArgumentError, arg_idx: usize) -> ExecutionError { + ExecutionError::from_kind(ExecutionErrorKind::command_argument_error( + e, + arg_idx as u16, + )) +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/event.rs b/identity_iota_interaction/src/sdk_types/iota_types/event.rs new file mode 100644 index 0000000000..a9f4b5527b --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/event.rs @@ -0,0 +1,55 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use anyhow::ensure; + +use super::{ + digests::TransactionDigest, + iota_serde::{Readable, BigInt}, +}; + +/// Unique ID of a Iota Event, the ID is a combination of tx seq number and +/// event seq number, the ID is local to this particular fullnode and will be +/// different from other fullnode. +#[serde_as] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)] +#[serde(rename_all = "camelCase")] +pub struct EventID { + pub tx_digest: TransactionDigest, + #[serde_as(as = "Readable, _>")] + pub event_seq: u64, +} + +impl From<(TransactionDigest, u64)> for EventID { + fn from((tx_digest_num, event_seq_number): (TransactionDigest, u64)) -> Self { + Self { + tx_digest: tx_digest_num as TransactionDigest, + event_seq: event_seq_number, + } + } +} + +impl From for String { + fn from(id: EventID) -> Self { + format!("{:?}:{}", id.tx_digest, id.event_seq) + } +} + +impl TryFrom for EventID { + type Error = anyhow::Error; + + fn try_from(value: String) -> Result { + let values = value.split(':').collect::>(); + ensure!(values.len() == 2, "Malformed EventID : {value}"); + Ok(( + TransactionDigest::from_str(values[0])?, + u64::from_str(values[1])?, + ) + .into()) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs b/identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs new file mode 100644 index 0000000000..6eba59db81 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs @@ -0,0 +1,341 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::{Display, Formatter}; + +use super::super::move_core_types::language_storage::ModuleId; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use super::base_types::{ObjectID, TypeParameterIndex, CodeOffset}; + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ExecutionStatus { + Success, + /// Gas used in the failed case, and the error. + Failure { + /// The error + error: ExecutionFailureStatus, + /// Which command the error occurred + command: Option, + }, +} + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error)] //, EnumVariantOrder)] +pub enum ExecutionFailureStatus { + // General transaction errors + #[error("Insufficient Gas.")] + InsufficientGas, + #[error("Invalid Gas Object. Possibly not address-owned or possibly not a IOTA coin.")] + InvalidGasObject, + #[error("INVARIANT VIOLATION.")] + InvariantViolation, + #[error("Attempted to used feature that is not supported yet")] + FeatureNotYetSupported, + #[error( + "Move object with size {object_size} is larger \ + than the maximum object size {max_object_size}" + )] + MoveObjectTooBig { + object_size: u64, + max_object_size: u64, + }, + #[error( + "Move package with size {object_size} is larger than the \ + maximum object size {max_object_size}" + )] + MovePackageTooBig { + object_size: u64, + max_object_size: u64, + }, + #[error("Circular Object Ownership, including object {object}.")] + CircularObjectOwnership { object: ObjectID }, + + // Coin errors + #[error("Insufficient coin balance for operation.")] + InsufficientCoinBalance, + #[error("The coin balance overflows u64")] + CoinBalanceOverflow, + + // Publish/Upgrade errors + #[error( + "Publish Error, Non-zero Address. \ + The modules in the package must have their self-addresses set to zero." + )] + PublishErrorNonZeroAddress, + + #[error( + "Iota Move Bytecode Verification Error. \ + Please run the Iota Move Verifier for more information." + )] + IotaMoveVerificationError, + + // Errors from the Move VM + // + // Indicates an error from a non-abort instruction + #[error( + "Move Primitive Runtime Error. Location: {0}. \ + Arithmetic error, stack overflow, max value depth, etc." + )] + MovePrimitiveRuntimeError(MoveLocationOpt), + #[error("Move Runtime Abort. Location: {0}, Abort Code: {1}")] + MoveAbort(MoveLocation, u64), + #[error( + "Move Bytecode Verification Error. \ + Please run the Bytecode Verifier for more information." + )] + VMVerificationOrDeserializationError, + #[error("MOVE VM INVARIANT VIOLATION.")] + VMInvariantViolation, + + // Programmable Transaction Errors + #[error("Function Not Found.")] + FunctionNotFound, + #[error( + "Arity mismatch for Move function. \ + The number of arguments does not match the number of parameters" + )] + ArityMismatch, + #[error( + "Type arity mismatch for Move function. \ + Mismatch between the number of actual versus expected type arguments." + )] + TypeArityMismatch, + #[error("Non Entry Function Invoked. Move Call must start with an entry function")] + NonEntryFunctionInvoked, + #[error("Invalid command argument at {arg_idx}. {kind}")] + CommandArgumentError { + arg_idx: u16, + kind: CommandArgumentError, + }, + #[error("Error for type argument at index {argument_idx}: {kind}")] + TypeArgumentError { + argument_idx: TypeParameterIndex, + kind: TypeArgumentError, + }, + #[error( + "Unused result without the drop ability. \ + Command result {result_idx}, return value {secondary_idx}" + )] + UnusedValueWithoutDrop { result_idx: u16, secondary_idx: u16 }, + #[error( + "Invalid public Move function signature. \ + Unsupported return type for return value {idx}" + )] + InvalidPublicFunctionReturnType { idx: u16 }, + #[error("Invalid Transfer Object, object does not have public transfer.")] + InvalidTransferObject, + + // Post-execution errors + // + // Indicates the effects from the transaction are too large + #[error( + "Effects of size {current_size} bytes too large. \ + Limit is {max_size} bytes" + )] + EffectsTooLarge { current_size: u64, max_size: u64 }, + + #[error( + "Publish/Upgrade Error, Missing dependency. \ + A dependency of a published or upgraded package has not been assigned an on-chain \ + address." + )] + PublishUpgradeMissingDependency, + + #[error( + "Publish/Upgrade Error, Dependency downgrade. \ + Indirect (transitive) dependency of published or upgraded package has been assigned an \ + on-chain version that is less than the version required by one of the package's \ + transitive dependencies." + )] + PublishUpgradeDependencyDowngrade, + + #[error("Invalid package upgrade. {upgrade_error}")] + PackageUpgradeError { upgrade_error: PackageUpgradeError }, + + // Indicates the transaction tried to write objects too large to storage + #[error( + "Written objects of {current_size} bytes too large. \ + Limit is {max_size} bytes" + )] + WrittenObjectsTooLarge { current_size: u64, max_size: u64 }, + + #[error("Certificate is on the deny list")] + CertificateDenied, + + #[error( + "Iota Move Bytecode Verification Timeout. \ + Please run the Iota Move Verifier for more information." + )] + IotaMoveVerificationTimedout, + + #[error("The shared object operation is not allowed.")] + SharedObjectOperationNotAllowed, + + #[error("Certificate cannot be executed due to a dependency on a deleted shared object")] + InputObjectDeleted, + // NOTE: if you want to add a new enum, + // please add it at the end for Rust SDK backward compatibility. +} + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)] +pub struct MoveLocation { + pub module: ModuleId, + pub function: u16, + pub instruction: CodeOffset, + pub function_name: Option, +} + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)] +pub struct MoveLocationOpt(pub Option); + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash, Error)] +pub enum CommandArgumentError { + #[error("The type of the value does not match the expected type")] + TypeMismatch, + #[error("The argument cannot be deserialized into a value of the specified type")] + InvalidBCSBytes, + #[error("The argument cannot be instantiated from raw bytes")] + InvalidUsageOfPureArg, + #[error( + "Invalid argument to private entry function. \ + These functions cannot take arguments from other Move functions" + )] + InvalidArgumentToPrivateEntryFunction, + #[error("Out of bounds access to input or result vector {idx}")] + IndexOutOfBounds { idx: u16 }, + #[error( + "Out of bounds secondary access to result vector \ + {result_idx} at secondary index {secondary_idx}" + )] + SecondaryIndexOutOfBounds { result_idx: u16, secondary_idx: u16 }, + #[error( + "Invalid usage of result {result_idx}, \ + expected a single result but found either no return values or multiple." + )] + InvalidResultArity { result_idx: u16 }, + #[error( + "Invalid taking of the Gas coin. \ + It can only be used by-value with TransferObjects" + )] + InvalidGasCoinUsage, + #[error( + "Invalid usage of value. \ + Mutably borrowed values require unique usage. \ + Immutably borrowed values cannot be taken or borrowed mutably. \ + Taken values cannot be used again." + )] + InvalidValueUsage, + #[error("Immutable objects cannot be passed by-value.")] + InvalidObjectByValue, + #[error("Immutable objects cannot be passed by mutable reference, &mut.")] + InvalidObjectByMutRef, + #[error( + "Shared object operations such a wrapping, freezing, or converting to owned are not \ + allowed." + )] + SharedObjectOperationNotAllowed, +} + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash, Error)] +pub enum PackageUpgradeError { + #[error("Unable to fetch package at {package_id}")] + UnableToFetchPackage { package_id: ObjectID }, + #[error("Object {object_id} is not a package")] + NotAPackage { object_id: ObjectID }, + #[error("New package is incompatible with previous version")] + IncompatibleUpgrade, + #[error("Digest in upgrade ticket and computed digest disagree")] + DigestDoesNotMatch { digest: Vec }, + #[error("Upgrade policy {policy} is not a valid upgrade policy")] + UnknownUpgradePolicy { policy: u8 }, + #[error("Package ID {package_id} does not match package ID in upgrade ticket {ticket_id}")] + PackageIDDoesNotMatch { + package_id: ObjectID, + ticket_id: ObjectID, + }, +} + +#[derive(Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize, Hash, Error)] +pub enum TypeArgumentError { + #[error("A type was not found in the module specified.")] + TypeNotFound, + #[error("A type provided did not match the specified constraints.")] + ConstraintNotSatisfied, +} + +impl ExecutionFailureStatus { + pub fn command_argument_error(kind: CommandArgumentError, arg_idx: u16) -> Self { + Self::CommandArgumentError { arg_idx, kind } + } +} + +impl Display for MoveLocationOpt { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self.0 { + None => write!(f, "UNKNOWN"), + Some(l) => write!(f, "{l}"), + } + } +} + +impl Display for MoveLocation { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let Self { + module, + function, + instruction, + function_name, + } = self; + if let Some(fname) = function_name { + write!( + f, + "{module}::{fname} (function index {function}) at offset {instruction}" + ) + } else { + write!( + f, + "{module} in function definition {function} at offset {instruction}" + ) + } + } +} + +impl ExecutionStatus { + pub fn new_failure( + error: ExecutionFailureStatus, + command: Option, + ) -> ExecutionStatus { + ExecutionStatus::Failure { error, command } + } + + pub fn is_ok(&self) -> bool { + matches!(self, ExecutionStatus::Success { .. }) + } + + pub fn is_err(&self) -> bool { + matches!(self, ExecutionStatus::Failure { .. }) + } + + pub fn unwrap(&self) { + match self { + ExecutionStatus::Success => {} + ExecutionStatus::Failure { .. } => { + panic!("Unable to unwrap() on {:?}", self); + } + } + } + + pub fn unwrap_err(self) -> (ExecutionFailureStatus, Option) { + match self { + ExecutionStatus::Success { .. } => { + panic!("Unable to unwrap() on {:?}", self); + } + ExecutionStatus::Failure { error, command } => (error, command), + } + } +} + +pub type CommandIndex = usize; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs b/identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs new file mode 100644 index 0000000000..528db7949c --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs @@ -0,0 +1,136 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Deserialize; +use serde::Serialize; + +use crate::ident_str; + +use super::super::move_core_types::language_storage::{StructTag, TypeTag}; +use super::super::move_core_types::identifier::IdentStr; +use super::super::move_core_types::annotated_value::MoveStructLayout; + +use super::super::types::IOTA_FRAMEWORK_ADDRESS; + +use super::coin::{Coin, TreasuryCap}; +use super::base_types::{ObjectID}; +use super::id::UID; +use super::balance::{Balance, Supply}; +use std::fmt::{Display, Formatter}; + +/// The number of Nanos per Iota token +pub const NANOS_PER_IOTA: u64 = 1_000_000_000; + +/// Total supply in IOTA at genesis, after the migration from a Stardust ledger, +/// before any inflation mechanism +pub const STARDUST_TOTAL_SUPPLY_IOTA: u64 = 4_600_000_000; + +// Note: cannot use checked arithmetic here since `const unwrap` is still +// unstable. +/// Total supply at genesis denominated in Nanos, after the migration from a +/// Stardust ledger, before any inflation mechanism +pub const STARDUST_TOTAL_SUPPLY_NANOS: u64 = STARDUST_TOTAL_SUPPLY_IOTA * NANOS_PER_IOTA; + +pub const GAS_MODULE_NAME: &IdentStr = ident_str!("iota"); +pub const GAS_STRUCT_NAME: &IdentStr = ident_str!("IOTA"); +pub const GAS_TREASURY_CAP_STRUCT_NAME: &IdentStr = ident_str!("IotaTreasuryCap"); + +pub struct GAS {} +impl GAS { + pub fn type_() -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + name: GAS_STRUCT_NAME.to_owned(), + module: GAS_MODULE_NAME.to_owned(), + type_params: Vec::new(), + } + } + + pub fn type_tag() -> TypeTag { + TypeTag::Struct(Box::new(Self::type_())) + } + + pub fn is_gas(other: &StructTag) -> bool { + &Self::type_() == other + } + + pub fn is_gas_type(other: &TypeTag) -> bool { + match other { + TypeTag::Struct(s) => Self::is_gas(s), + _ => false, + } + } +} + +/// Rust version of the Move iota::coin::Coin type +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct GasCoin(pub Coin); + +impl GasCoin { + pub fn new(id: ObjectID, value: u64) -> Self { + Self(Coin::new(UID::new(id), value)) + } + + pub fn value(&self) -> u64 { + self.0.value() + } + + pub fn type_() -> StructTag { + Coin::type_(TypeTag::Struct(Box::new(GAS::type_()))) + } + + /// Return `true` if `s` is the type of a gas coin (i.e., + /// 0x2::coin::Coin<0x2::iota::IOTA>) + pub fn is_gas_coin(s: &StructTag) -> bool { + Coin::is_coin(s) && s.type_params.len() == 1 && GAS::is_gas_type(&s.type_params[0]) + } + + /// Return `true` if `s` is the type of a gas balance (i.e., + /// 0x2::balance::Balance<0x2::iota::IOTA>) + pub fn is_gas_balance(s: &StructTag) -> bool { + Balance::is_balance(s) + && s.type_params.len() == 1 + && GAS::is_gas_type(&s.type_params[0]) + } + + pub fn id(&self) -> &ObjectID { + self.0.id() + } + + pub fn to_bcs_bytes(&self) -> Vec { + bcs::to_bytes(&self).unwrap() + } + + pub fn layout() -> MoveStructLayout { + Coin::layout(TypeTag::Struct(Box::new(GAS::type_()))) + } +} + +impl Display for GasCoin { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Coin {{ id: {}, value: {} }}", self.id(), self.value()) + } +} + +// Rust version of the IotaTreasuryCap type +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct IotaTreasuryCap { + pub inner: TreasuryCap, +} + +impl IotaTreasuryCap { + pub fn type_() -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + module: GAS_MODULE_NAME.to_owned(), + name: GAS_TREASURY_CAP_STRUCT_NAME.to_owned(), + type_params: Vec::new(), + } + } + + /// Returns the total `Supply` of `Coin`. + pub fn total_supply(&self) -> &Supply { + &self.inner.total_supply + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/governance.rs b/identity_iota_interaction/src/sdk_types/iota_types/governance.rs new file mode 100644 index 0000000000..e9ed0fe3e8 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/governance.rs @@ -0,0 +1,98 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Deserialize; +use serde::Serialize; + +use crate::ident_str; + +use super::super::move_core_types::language_storage::StructTag; +use super::super::move_core_types::identifier::IdentStr; + +use super::gas_coin::NANOS_PER_IOTA; +use super::balance::Balance; +use super::IOTA_SYSTEM_ADDRESS; +use super::base_types::{ObjectID, EpochId}; +use super::id::{UID, ID}; + +/// Maximum number of active validators at any moment. +/// We do not allow the number of validators in any epoch to go above this. +pub const MAX_VALIDATOR_COUNT: u64 = 150; + +/// Lower-bound on the amount of stake required to become a validator. +/// +/// 2 million IOTA +pub const MIN_VALIDATOR_JOINING_STAKE_NANOS: u64 = 2_000_000 * NANOS_PER_IOTA; + +/// Validators with stake amount below `validator_low_stake_threshold` are +/// considered to have low stake and will be escorted out of the validator set +/// after being below this threshold for more than +/// `validator_low_stake_grace_period` number of epochs. +/// +/// 1.5 million IOTA +pub const VALIDATOR_LOW_STAKE_THRESHOLD_NANOS: u64 = 1_500_000 * NANOS_PER_IOTA; + +/// Validators with stake below `validator_very_low_stake_threshold` will be +/// removed immediately at epoch change, no grace period. +/// +/// 1 million IOTA +pub const VALIDATOR_VERY_LOW_STAKE_THRESHOLD_NANOS: u64 = 1_000_000 * NANOS_PER_IOTA; + +/// A validator can have stake below `validator_low_stake_threshold` +/// for this many epochs before being kicked out. +pub const VALIDATOR_LOW_STAKE_GRACE_PERIOD: u64 = 7; + +pub const STAKING_POOL_MODULE_NAME: &IdentStr = ident_str!("staking_pool"); +pub const STAKED_IOTA_STRUCT_NAME: &IdentStr = ident_str!("StakedIota"); + +pub const ADD_STAKE_MUL_COIN_FUN_NAME: &IdentStr = ident_str!("request_add_stake_mul_coin"); +pub const ADD_STAKE_FUN_NAME: &IdentStr = ident_str!("request_add_stake"); +pub const WITHDRAW_STAKE_FUN_NAME: &IdentStr = ident_str!("request_withdraw_stake"); + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct StakedIota { + id: UID, + pool_id: ID, + stake_activation_epoch: u64, + principal: Balance, +} + +impl StakedIota { + pub fn type_() -> StructTag { + StructTag { + address: IOTA_SYSTEM_ADDRESS, + module: STAKING_POOL_MODULE_NAME.to_owned(), + name: STAKED_IOTA_STRUCT_NAME.to_owned(), + type_params: vec![], + } + } + + pub fn is_staked_iota(s: &StructTag) -> bool { + s.address == IOTA_SYSTEM_ADDRESS + && s.module.as_ident_str() == STAKING_POOL_MODULE_NAME + && s.name.as_ident_str() == STAKED_IOTA_STRUCT_NAME + && s.type_params.is_empty() + } + + pub fn id(&self) -> ObjectID { + self.id.id.bytes + } + + pub fn pool_id(&self) -> ObjectID { + self.pool_id.bytes + } + + pub fn activation_epoch(&self) -> EpochId { + self.stake_activation_epoch + } + + pub fn request_epoch(&self) -> EpochId { + // TODO: this might change when we implement warm up period. + self.stake_activation_epoch.saturating_sub(1) + } + + pub fn principal(&self) -> u64 { + self.principal.value() + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/id.rs b/identity_iota_interaction/src/sdk_types/iota_types/id.rs new file mode 100644 index 0000000000..ef731c99c7 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/id.rs @@ -0,0 +1,108 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; + +use crate::ident_str; + +use super::super::{ + move_core_types::{ + account_address::AccountAddress, + identifier::IdentStr, + language_storage::{StructTag, TypeTag}, + annotated_value::{MoveStructLayout, MoveFieldLayout, MoveTypeLayout}, + }, +}; + +use super::{ + base_types::ObjectID, + MoveTypeTagTrait, + IOTA_FRAMEWORK_ADDRESS, +}; + +pub const OBJECT_MODULE_NAME_STR: &str = "object"; +pub const OBJECT_MODULE_NAME: &IdentStr = ident_str!(OBJECT_MODULE_NAME_STR); +pub const UID_STRUCT_NAME: &IdentStr = ident_str!("UID"); +pub const ID_STRUCT_NAME: &IdentStr = ident_str!("ID"); +pub const RESOLVED_IOTA_ID: (&AccountAddress, &IdentStr, &IdentStr) = + (&IOTA_FRAMEWORK_ADDRESS, OBJECT_MODULE_NAME, ID_STRUCT_NAME); + +/// Rust version of the Move iota::object::Info type +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct UID { + pub id: ID, +} + +/// Rust version of the Move iota::object::ID type +#[derive(Debug, Hash, Serialize, Deserialize, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct ID { + pub bytes: ObjectID, +} + +impl UID { + pub fn new(bytes: ObjectID) -> Self { + Self { + id: { ID::new(bytes) }, + } + } + + pub fn type_() -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + module: OBJECT_MODULE_NAME.to_owned(), + name: UID_STRUCT_NAME.to_owned(), + type_params: Vec::new(), + } + } + + pub fn object_id(&self) -> &ObjectID { + &self.id.bytes + } + + pub fn to_bcs_bytes(&self) -> Vec { + bcs::to_bytes(&self).unwrap() + } + + pub fn layout() -> MoveStructLayout { + MoveStructLayout { + type_: Self::type_(), + fields: vec![MoveFieldLayout::new( + ident_str!("id").to_owned(), + MoveTypeLayout::Struct(ID::layout()), + )], + } + } +} + +impl ID { + pub fn new(object_id: ObjectID) -> Self { + Self { bytes: object_id } + } + + pub fn type_() -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + module: OBJECT_MODULE_NAME.to_owned(), + name: ID_STRUCT_NAME.to_owned(), + type_params: Vec::new(), + } + } + + pub fn layout() -> MoveStructLayout { + MoveStructLayout { + type_: Self::type_(), + fields: vec![MoveFieldLayout::new( + ident_str!("bytes").to_owned(), + MoveTypeLayout::Address, + )], + } + } +} + +impl MoveTypeTagTrait for ID { + fn get_type_tag() -> TypeTag { + TypeTag::Struct(Box::new(Self::type_())) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs b/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs new file mode 100644 index 0000000000..8c50bcb1e5 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs @@ -0,0 +1,415 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + fmt, + fmt::{Debug, Display, Formatter, Write}, + marker::PhantomData, + ops::Deref, + str::FromStr, +}; +use std::marker::Sized; +use std::string::{String, ToString}; +use std::result::Result::Ok; +use std::option::Option; +use std::option::Option::Some; + +use fastcrypto::encoding::Hex; +use serde::{ + self, + de::{Deserializer, Error}, + ser::{Error as SerError, Serializer}, + Deserialize, Serialize, +}; +use serde_with::{serde_as, DeserializeAs, DisplayFromStr, SerializeAs}; + +use Result; + +use super::super::move_core_types::{ + account_address::AccountAddress, + language_storage::{StructTag, TypeTag} +}; +// use super::address::ParsedAddress; +use super::{IOTA_FRAMEWORK_ADDRESS, DEEPBOOK_ADDRESS, MOVE_STDLIB_ADDRESS, IOTA_SYSTEM_ADDRESS, + STARDUST_ADDRESS, IOTA_SYSTEM_STATE_ADDRESS, IOTA_CLOCK_ADDRESS}; + +/// The minimum and maximum protocol versions supported by this build. +const MIN_PROTOCOL_VERSION: u64 = 1; +pub const MAX_PROTOCOL_VERSION: u64 = 1; + +/// Resolve well-known named addresses into numeric addresses. +pub fn resolve_address(addr: &str) -> Option { + match addr { + "deepbook" => Some(DEEPBOOK_ADDRESS), + "std" => Some(MOVE_STDLIB_ADDRESS), + "iota" => Some(IOTA_FRAMEWORK_ADDRESS), + "iota_system" => Some(IOTA_SYSTEM_ADDRESS), + "stardust" => Some(STARDUST_ADDRESS), + _ => None, + } +} + +/// Parse `s` as a struct type: A fully-qualified name, optionally followed by a +/// list of type parameters (types -- see `parse_iota_type_tag`, separated by +/// commas, surrounded by angle brackets). Parsing succeeds if and only if `s` +/// matches this format exactly, with no remaining input. This function is +/// intended for use within the authority codebase. +pub fn parse_iota_struct_tag(s: &str) -> anyhow::Result { + use super::super::move_command_line_common::types::ParsedStructType; + ParsedStructType::parse(s)?.into_struct_tag(&resolve_address) +} + +/// Parse `s` as a type: Either a struct type (see `parse_iota_struct_tag`), a +/// primitive type, or a vector with a type parameter. Parsing succeeds if and +/// only if `s` matches this format exactly, with no remaining input. This +/// function is intended for use within the authority codebase. +pub fn parse_iota_type_tag(s: &str) -> anyhow::Result { + use super::super::move_command_line_common::types::ParsedType; + ParsedType::parse(s)?.into_type_tag(&resolve_address) +} + +#[inline] +fn to_custom_error<'de, D, E>(e: E) -> D::Error + where + E: Debug, + D: Deserializer<'de>, +{ + Error::custom(format!("byte deserialization failed, cause by: {:?}", e)) +} + +/// Use with serde_as to control serde for human-readable serialization and +/// deserialization `H` : serde_as SerializeAs/DeserializeAs delegation for +/// human readable in/output `R` : serde_as SerializeAs/DeserializeAs delegation +/// for non-human readable in/output +/// +/// # Example: +/// +/// ```text +/// #[serde_as] +/// #[derive(Deserialize, Serialize)] +/// struct Example(#[serde_as(as = "Readable")] [u8; 20]); +/// ``` +/// +/// The above example will delegate human-readable serde to `DisplayFromStr` +/// and array tuple (default) for non-human-readable serializer. +pub struct Readable { + human_readable: PhantomData, + non_human_readable: PhantomData, +} + +impl SerializeAs for Readable + where + H: SerializeAs, + R: SerializeAs, +{ + fn serialize_as(value: &T, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + H::serialize_as(value, serializer) + } else { + R::serialize_as(value, serializer) + } + } +} + +impl<'de, R, H, T> DeserializeAs<'de, T> for Readable + where + H: DeserializeAs<'de, T>, + R: DeserializeAs<'de, T>, +{ + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + H::deserialize_as(deserializer) + } else { + R::deserialize_as(deserializer) + } + } +} + +/// custom serde for AccountAddress +pub struct HexAccountAddress; + +impl SerializeAs for HexAccountAddress { + fn serialize_as(value: &AccountAddress, serializer: S) -> Result + where + S: Serializer, + { + Hex::serialize_as(value, serializer) + } +} + +impl<'de> DeserializeAs<'de, AccountAddress> for HexAccountAddress { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if s.starts_with("0x") { + AccountAddress::from_hex_literal(&s) + } else { + AccountAddress::from_hex(&s) + } + .map_err(to_custom_error::<'de, D, _>) + } +} + +/// Serializes a bitmap according to the roaring bitmap on-disk standard. +/// +pub struct IotaBitmap; + +pub struct IotaStructTag; + +impl SerializeAs for IotaStructTag { + fn serialize_as(value: &StructTag, serializer: S) -> Result + where + S: Serializer, + { + let f = to_iota_struct_tag_string(value).map_err(S::Error::custom)?; + f.serialize(serializer) + } +} + +const IOTA_ADDRESSES: [AccountAddress; 8] = [ + AccountAddress::ZERO, + AccountAddress::ONE, + IOTA_FRAMEWORK_ADDRESS, + IOTA_SYSTEM_ADDRESS, + STARDUST_ADDRESS, + DEEPBOOK_ADDRESS, + IOTA_SYSTEM_STATE_ADDRESS, + IOTA_CLOCK_ADDRESS, +]; +/// Serialize StructTag as a string, retaining the leading zeros in the address. +pub fn to_iota_struct_tag_string(value: &StructTag) -> Result { + let mut f = String::new(); + // trim leading zeros if address is in IOTA_ADDRESSES + let address = if IOTA_ADDRESSES.contains(&value.address) { + value.address.short_str_lossless() + } else { + value.address.to_canonical_string(/* with_prefix */ false) + }; + + write!(f, "0x{}::{}::{}", address, value.module, value.name)?; + if let Some(first_ty) = value.type_params.first() { + write!(f, "<")?; + write!(f, "{}", to_iota_type_tag_string(first_ty)?)?; + for ty in value.type_params.iter().skip(1) { + write!(f, ", {}", to_iota_type_tag_string(ty)?)?; + } + write!(f, ">")?; + } + Ok(f) +} + +fn to_iota_type_tag_string(value: &TypeTag) -> Result { + match value { + TypeTag::Vector(t) => Ok(format!("vector<{}>", to_iota_type_tag_string(t)?)), + TypeTag::Struct(s) => to_iota_struct_tag_string(s), + _ => Ok(value.to_string()), + } +} + +impl<'de> DeserializeAs<'de, StructTag> for IotaStructTag { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + parse_iota_struct_tag(&s).map_err(D::Error::custom) + } +} + +pub struct IotaTypeTag; + +impl SerializeAs for IotaTypeTag { + fn serialize_as(value: &TypeTag, serializer: S) -> Result + where + S: Serializer, + { + let s = to_iota_type_tag_string(value).map_err(S::Error::custom)?; + s.serialize(serializer) + } +} + +impl<'de> DeserializeAs<'de, TypeTag> for IotaTypeTag { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + parse_iota_type_tag(&s).map_err(D::Error::custom) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)] +pub struct BigInt( + #[serde_as(as = "DisplayFromStr")] + T, +) + where + T: Display + FromStr, + ::Err: Display; + +impl BigInt + where + T: Display + FromStr, + ::Err: Display, +{ + pub fn into_inner(self) -> T { + self.0 + } +} + +impl SerializeAs for BigInt + where + T: Display + FromStr + Copy, + ::Err: Display, +{ + fn serialize_as(value: &T, serializer: S) -> Result + where + S: Serializer, + { + BigInt(*value).serialize(serializer) + } +} + +impl<'de, T> DeserializeAs<'de, T> for BigInt + where + T: Display + FromStr + Copy, + ::Err: Display, +{ + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(*BigInt::deserialize(deserializer)?) + } +} + +impl From for BigInt + where + T: Display + FromStr, + ::Err: Display, +{ + fn from(v: T) -> BigInt { + BigInt(v) + } +} + +impl Deref for BigInt + where + T: Display + FromStr, + ::Err: Display, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for BigInt + where + T: Display + FromStr, + ::Err: Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)] +pub struct SequenceNumber(u64); + +impl SerializeAs for SequenceNumber { + fn serialize_as( + value: &super::base_types::SequenceNumber, + serializer: S, + ) -> Result + where + S: Serializer, + { + let s = value.value().to_string(); + s.serialize(serializer) + } +} + +impl<'de> DeserializeAs<'de, super::base_types::SequenceNumber> for SequenceNumber { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let b = BigInt::deserialize(deserializer)?; + Ok(super::base_types::SequenceNumber::from_u64(*b)) + } +} + +// Record history of protocol version allocations here: +// +// Version 1: Original version. +#[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct ProtocolVersion(u64); + +impl ProtocolVersion { + // The minimum and maximum protocol version supported by this binary. + // Counterintuitively, this constant may change over time as support for old + // protocol versions is removed from the source. This ensures that when a + // new network (such as a testnet) is created, its genesis committee will + // use a protocol version that is actually supported by the binary. + pub const MIN: Self = Self(MIN_PROTOCOL_VERSION); + + pub const MAX: Self = Self(MAX_PROTOCOL_VERSION); + + pub fn new(v: u64) -> Self { + Self(v) + } + + pub const fn as_u64(&self) -> u64 { + self.0 + } + + // For serde deserialization - we don't define a Default impl because there + // isn't a single universally appropriate default value. + pub fn max() -> Self { + Self::MAX + } +} + +impl From for ProtocolVersion { + fn from(v: u64) -> Self { + Self::new(v) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)] +#[serde(rename = "ProtocolVersion")] +pub struct AsProtocolVersion(u64); + +impl SerializeAs for AsProtocolVersion { + fn serialize_as(value: &ProtocolVersion, serializer: S) -> Result + where + S: Serializer, + { + let s = value.as_u64().to_string(); + s.serialize(serializer) + } +} + +impl<'de> DeserializeAs<'de, ProtocolVersion> for AsProtocolVersion { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let b = BigInt::::deserialize(deserializer)?; + Ok(ProtocolVersion::from(*b)) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs b/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs new file mode 100644 index 0000000000..ee5edb31e8 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs @@ -0,0 +1,124 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use super::super::move_core_types::{ + account_address::AccountAddress, + language_storage::TypeTag, +}; +use super::base_types::{ObjectID, SequenceNumber, IotaAddress}; +use super::object::OBJECT_START_VERSION; + +/// 0x1-- account address where Move stdlib modules are stored +/// Same as the ObjectID +pub const MOVE_STDLIB_ADDRESS: AccountAddress = AccountAddress::ONE; +pub const MOVE_STDLIB_PACKAGE_ID: ObjectID = ObjectID::from_address(MOVE_STDLIB_ADDRESS); + +/// 0x2-- account address where iota framework modules are stored +/// Same as the ObjectID +pub const IOTA_FRAMEWORK_ADDRESS: AccountAddress = address_from_single_byte(2); +pub const IOTA_FRAMEWORK_PACKAGE_ID: ObjectID = ObjectID::from_address(IOTA_FRAMEWORK_ADDRESS); + +/// 0x3-- account address where iota system modules are stored +/// Same as the ObjectID +pub const IOTA_SYSTEM_ADDRESS: AccountAddress = address_from_single_byte(3); +pub const IOTA_SYSTEM_PACKAGE_ID: ObjectID = ObjectID::from_address(IOTA_SYSTEM_ADDRESS); + +/// 0xdee9-- account address where DeepBook modules are stored +/// Same as the ObjectID +pub const DEEPBOOK_ADDRESS: AccountAddress = deepbook_addr(); +pub const DEEPBOOK_PACKAGE_ID: ObjectID = ObjectID::from_address(DEEPBOOK_ADDRESS); + +/// 0x107a-- account address where Stardust modules are stored +/// Same as the ObjectID +pub const STARDUST_ADDRESS: AccountAddress = stardust_addr(); +pub const STARDUST_PACKAGE_ID: ObjectID = ObjectID::from_address(STARDUST_ADDRESS); + +/// 0x5: hardcoded object ID for the singleton iota system state object. +pub const IOTA_SYSTEM_STATE_ADDRESS: AccountAddress = address_from_single_byte(5); +pub const IOTA_SYSTEM_STATE_OBJECT_ID: ObjectID = ObjectID::from_address(IOTA_SYSTEM_STATE_ADDRESS); +pub const IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; + +/// 0x6: hardcoded object ID for the singleton clock object. +pub const IOTA_CLOCK_ADDRESS: AccountAddress = address_from_single_byte(6); +pub const IOTA_CLOCK_OBJECT_ID: ObjectID = ObjectID::from_address(IOTA_CLOCK_ADDRESS); +pub const IOTA_CLOCK_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; + +/// 0x7: hardcode object ID for the singleton authenticator state object. +pub const IOTA_AUTHENTICATOR_STATE_ADDRESS: AccountAddress = address_from_single_byte(7); +pub const IOTA_AUTHENTICATOR_STATE_OBJECT_ID: ObjectID = + ObjectID::from_address(IOTA_AUTHENTICATOR_STATE_ADDRESS); +pub const IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; + +/// 0x8: hardcode object ID for the singleton randomness state object. +pub const IOTA_RANDOMNESS_STATE_ADDRESS: AccountAddress = address_from_single_byte(8); +pub const IOTA_RANDOMNESS_STATE_OBJECT_ID: ObjectID = + ObjectID::from_address(IOTA_RANDOMNESS_STATE_ADDRESS); + +/// 0x403: hardcode object ID for the singleton DenyList object. +pub const IOTA_DENY_LIST_ADDRESS: AccountAddress = deny_list_addr(); +pub const IOTA_DENY_LIST_OBJECT_ID: ObjectID = ObjectID::from_address(IOTA_DENY_LIST_ADDRESS); + +const fn address_from_single_byte(b: u8) -> AccountAddress { + let mut addr = [0u8; AccountAddress::LENGTH]; + addr[AccountAddress::LENGTH - 1] = b; + AccountAddress::new(addr) +} + +/// return 0x0...dee9 +pub(crate) const fn deepbook_addr() -> AccountAddress { + let mut addr = [0u8; AccountAddress::LENGTH]; + addr[AccountAddress::LENGTH - 2] = 0xde; + addr[AccountAddress::LENGTH - 1] = 0xe9; + AccountAddress::new(addr) +} + +/// return 0x0...107a +const fn stardust_addr() -> AccountAddress { + let mut addr = [0u8; AccountAddress::LENGTH]; + addr[AccountAddress::LENGTH - 2] = 0x10; + addr[AccountAddress::LENGTH - 1] = 0x7a; + AccountAddress::new(addr) +} + +/// return 0x0...403 +const fn deny_list_addr() -> AccountAddress { + let mut addr = [0u8; AccountAddress::LENGTH]; + addr[AccountAddress::LENGTH - 2] = 4; + addr[AccountAddress::LENGTH - 1] = 3; + AccountAddress::new(addr) +} + +pub trait MoveTypeTagTrait { + fn get_type_tag() -> TypeTag; +} + +impl MoveTypeTagTrait for u8 { + fn get_type_tag() -> TypeTag { + TypeTag::U8 + } +} + +impl MoveTypeTagTrait for u64 { + fn get_type_tag() -> TypeTag { + TypeTag::U64 + } +} + +impl MoveTypeTagTrait for ObjectID { + fn get_type_tag() -> TypeTag { + TypeTag::Address + } +} + +impl MoveTypeTagTrait for IotaAddress { + fn get_type_tag() -> TypeTag { + TypeTag::Address + } +} + +impl MoveTypeTagTrait for Vec { + fn get_type_tag() -> TypeTag { + TypeTag::Vector(Box::new(T::get_type_tag())) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/mod.rs b/identity_iota_interaction/src/sdk_types/iota_types/mod.rs new file mode 100644 index 0000000000..bb5e55f349 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/mod.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub mod balance; +pub mod base_types; +pub mod coin; +pub mod collection_types; +pub mod crypto; +pub mod digests; +pub mod dynamic_field; +pub mod error; +pub mod execution_status; +pub mod gas_coin; +pub mod governance; +pub mod id; +pub mod iota_serde; +pub mod iota_types_lib; +pub mod move_package; +pub mod object; +pub mod quorum_driver_types; +pub mod stardust; +pub mod timelock; +pub mod transaction; +pub mod event; + +pub use iota_types_lib::*; +pub use super::move_core_types::{identifier::Identifier, language_storage::TypeTag}; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/move_package.rs b/identity_iota_interaction/src/sdk_types/iota_types/move_package.rs new file mode 100644 index 0000000000..0293333064 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/move_package.rs @@ -0,0 +1,82 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::BTreeMap; + +use serde::Deserialize; +use serde::Serialize; +use serde_with::{serde_as, Bytes}; + +use super::base_types::{ObjectID, SequenceNumber}; + +/// Identifies a struct and the module it was defined in +#[derive( +Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, Hash)] +pub struct TypeOrigin { + pub module_name: String, + pub struct_name: String, + pub package: ObjectID, +} + +/// Upgraded package info for the linkage table +#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)] +pub struct UpgradeInfo { + /// ID of the upgraded packages + pub upgraded_id: ObjectID, + /// Version of the upgraded package + pub upgraded_version: SequenceNumber, +} + +// serde_bytes::ByteBuf is an analog of Vec with built-in fast +// serialization. +#[serde_as] +#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)] +pub struct MovePackage { + id: ObjectID, + /// Most move packages are uniquely identified by their ID (i.e. there is + /// only one version per ID), but the version is still stored because + /// one package may be an upgrade of another (at a different ID), in + /// which case its version will be one greater than the version of the + /// upgraded package. + /// + /// Framework packages are an exception to this rule -- all versions of the + /// framework packages exist at the same ID, at increasing versions. + /// + /// In all cases, packages are referred to by move calls using just their + /// ID, and they are always loaded at their latest version. + version: SequenceNumber, + // TODO use session cache + #[serde_as(as = "BTreeMap<_, Bytes>")] + module_map: BTreeMap>, + + /// Maps struct/module to a package version where it was first defined, + /// stored as a vector for simple serialization and deserialization. + type_origin_table: Vec, + + // For each dependency, maps original package ID to the info about the (upgraded) dependency + // version that this package is using + linkage_table: BTreeMap, +} + +impl MovePackage { + pub fn id(&self) -> ObjectID { + self.id + } + + pub fn version(&self) -> SequenceNumber { + self.version + } + + pub fn serialized_module_map(&self) -> &BTreeMap> { + &self.module_map + } + + pub fn type_origin_table(&self) -> &Vec { + &self.type_origin_table + } + + pub fn linkage_table(&self) -> &BTreeMap { + &self.linkage_table + } +} diff --git a/identity_iota_interaction/src/sdk_types/iota_types/object.rs b/identity_iota_interaction/src/sdk_types/iota_types/object.rs new file mode 100644 index 0000000000..1bfab931ed --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/object.rs @@ -0,0 +1,107 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::{Display, Formatter}; + +use serde::Deserialize; +use serde::Serialize; + +use super::base_types::{IotaAddress, SequenceNumber, ObjectID}; +use super::error::{IotaResult, IotaError}; + +pub const OBJECT_START_VERSION: SequenceNumber = SequenceNumber::from_u64(1); + +#[derive( +Eq, PartialEq, Debug, Clone, Copy, Deserialize, Serialize, Hash, Ord, PartialOrd,)] +pub enum Owner { + /// Object is exclusively owned by a single address, and is mutable. + AddressOwner(IotaAddress), + /// Object is exclusively owned by a single object, and is mutable. + /// The object ID is converted to IotaAddress as IotaAddress is universal. + ObjectOwner(IotaAddress), + /// Object is shared, can be used by any address, and is mutable. + Shared { + /// The version at which the object became shared + initial_shared_version: SequenceNumber, + }, + /// Object is immutable, and hence ownership doesn't matter. + Immutable, +} + +impl Owner { + // NOTE: only return address of AddressOwner, otherwise return error, + // ObjectOwner's address is converted from object id, thus we will skip it. + pub fn get_address_owner_address(&self) -> IotaResult { + match self { + Self::AddressOwner(address) => Ok(*address), + Self::Shared { .. } | Self::Immutable | Self::ObjectOwner(_) => { + Err(IotaError::UnexpectedOwnerType) + } + } + } + + // NOTE: this function will return address of both AddressOwner and ObjectOwner, + // address of ObjectOwner is converted from object id, even though the type is + // IotaAddress. + pub fn get_owner_address(&self) -> IotaResult { + match self { + Self::AddressOwner(address) | Self::ObjectOwner(address) => Ok(*address), + Self::Shared { .. } | Self::Immutable => Err(IotaError::UnexpectedOwnerType), + } + } + + pub fn is_immutable(&self) -> bool { + matches!(self, Owner::Immutable) + } + + pub fn is_address_owned(&self) -> bool { + matches!(self, Owner::AddressOwner(_)) + } + + pub fn is_child_object(&self) -> bool { + matches!(self, Owner::ObjectOwner(_)) + } + + pub fn is_shared(&self) -> bool { + matches!(self, Owner::Shared { .. }) + } +} + +impl PartialEq for Owner { + fn eq(&self, other: &IotaAddress) -> bool { + match self { + Self::AddressOwner(address) => address == other, + Self::ObjectOwner(_) | Self::Shared { .. } | Self::Immutable => false, + } + } +} + +impl PartialEq for Owner { + fn eq(&self, other: &ObjectID) -> bool { + let other_id: IotaAddress = (*other).into(); + match self { + Self::ObjectOwner(id) => id == &other_id, + Self::AddressOwner(_) | Self::Shared { .. } | Self::Immutable => false, + } + } +} + +impl Display for Owner { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::AddressOwner(address) => { + write!(f, "Account Address ( {} )", address) + } + Self::ObjectOwner(address) => { + write!(f, "Object ID: ( {} )", address) + } + Self::Immutable => { + write!(f, "Immutable") + } + Self::Shared { .. } => { + write!(f, "Shared") + } + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/quorum_driver_types.rs b/identity_iota_interaction/src/sdk_types/iota_types/quorum_driver_types.rs new file mode 100644 index 0000000000..3eb37fcbbe --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/quorum_driver_types.rs @@ -0,0 +1,12 @@ +// Copyright (c) 2021, Facebook, Inc. and its affiliates +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum ExecuteTransactionRequestType { + WaitForEffectsCert, + WaitForLocalExecution, +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/stardust/mod.rs b/identity_iota_interaction/src/sdk_types/iota_types/stardust/mod.rs new file mode 100644 index 0000000000..435de6e4e3 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/stardust/mod.rs @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub mod nft; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/stardust/nft.rs b/identity_iota_interaction/src/sdk_types/iota_types/stardust/nft.rs new file mode 100644 index 0000000000..bf3176bbef --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/stardust/nft.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::ident_str; + +use super::super::super::move_core_types::language_storage::StructTag; +use super::super::super::move_core_types::identifier::IdentStr; +use super::super::STARDUST_PACKAGE_ID; + +pub const IRC27_MODULE_NAME: &IdentStr = ident_str!("irc27"); +pub const NFT_MODULE_NAME: &IdentStr = ident_str!("nft"); +pub const NFT_OUTPUT_MODULE_NAME: &IdentStr = ident_str!("nft_output"); +pub const NFT_OUTPUT_STRUCT_NAME: &IdentStr = ident_str!("NftOutput"); +pub const NFT_STRUCT_NAME: &IdentStr = ident_str!("Nft"); +pub const IRC27_STRUCT_NAME: &IdentStr = ident_str!("Irc27Metadata"); +pub const NFT_DYNAMIC_OBJECT_FIELD_KEY: &[u8] = b"nft"; +pub const NFT_DYNAMIC_OBJECT_FIELD_KEY_TYPE: &str = "vector"; + +pub struct Nft {} + +impl Nft { + /// Returns the struct tag that represents the fully qualified path of an + /// [`Nft`] in its move package. + pub fn tag() -> StructTag { + StructTag { + address: STARDUST_PACKAGE_ID.into(), + module: NFT_MODULE_NAME.to_owned(), + name: NFT_STRUCT_NAME.to_owned(), + type_params: Vec::new(), + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/timelock/mod.rs b/identity_iota_interaction/src/sdk_types/iota_types/timelock/mod.rs new file mode 100644 index 0000000000..9e825bb774 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/timelock/mod.rs @@ -0,0 +1,5 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub mod timelock; +pub mod timelocked_staked_iota; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs b/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs new file mode 100644 index 0000000000..6d44fa24a5 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs @@ -0,0 +1,187 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Deserialize; +use serde::Serialize; + +use crate::ident_str; + +use super::super::super::move_core_types::{ + language_storage::{TypeTag, StructTag}, + identifier::{IdentStr}, +}; + +use super::super::{ + IOTA_FRAMEWORK_ADDRESS, + IOTA_SYSTEM_ADDRESS, + base_types::{ObjectID, EpochId}, + balance::Balance, + governance::StakedIota, + id::UID, +}; + +use super::timelocked_staked_iota::{TIMELOCKED_STAKED_IOTA_MODULE_NAME, TIMELOCKED_STAKED_IOTA_STRUCT_NAME}; + +pub const TIMELOCK_MODULE_NAME: &IdentStr = ident_str!("timelock"); +pub const TIMELOCK_STRUCT_NAME: &IdentStr = ident_str!("TimeLock"); + +/// Rust version of the Move stardust::TimeLock type. +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct TimeLock { + id: UID, + /// The locked object. + locked: T, + /// This is the epoch time stamp of when the lock expires. + expiration_timestamp_ms: u64, + /// Timelock related label. + label: Option, +} + +impl TimeLock { + /// Constructor. + pub fn new(id: UID, locked: T, expiration_timestamp_ms: u64, label: Option) -> Self { + Self { + id, + locked, + expiration_timestamp_ms, + label, + } + } + + /// Get the TimeLock's `type`. + pub fn type_(type_param: TypeTag) -> StructTag { + StructTag { + address: IOTA_FRAMEWORK_ADDRESS, + module: TIMELOCK_MODULE_NAME.to_owned(), + name: TIMELOCK_STRUCT_NAME.to_owned(), + type_params: vec![type_param], + } + } + + /// Get the TimeLock's `id`. + pub fn id(&self) -> &ObjectID { + self.id.object_id() + } + + /// Get the TimeLock's `locked` object. + pub fn locked(&self) -> &T { + &self.locked + } + + /// Get the TimeLock's `expiration_timestamp_ms`. + pub fn expiration_timestamp_ms(&self) -> u64 { + self.expiration_timestamp_ms + } + + /// Get the TimeLock's `label``. + pub fn label(&self) -> &Option { + &self.label + } +} + +impl<'de, T> TimeLock + where + T: Serialize + Deserialize<'de>, +{ + /// Create a `TimeLock` from BCS bytes. + pub fn from_bcs_bytes(content: &'de [u8]) -> Result { + bcs::from_bytes(content) + } + + /// Serialize a `TimeLock` as a `Vec` of BCS. + pub fn to_bcs_bytes(&self) -> Vec { + bcs::to_bytes(&self).unwrap() + } +} + +/// Is this other StructTag representing a TimeLock? +pub fn is_timelock(other: &StructTag) -> bool { + other.address == IOTA_FRAMEWORK_ADDRESS + && other.module.as_ident_str() == TIMELOCK_MODULE_NAME + && other.name.as_ident_str() == TIMELOCK_STRUCT_NAME +} + +/// Is this other StructTag representing a `TimeLock>`? +pub fn is_timelocked_balance(other: &StructTag) -> bool { + if !is_timelock(other) { + return false; + } + + if other.type_params.len() != 1 { + return false; + } + + match &other.type_params[0] { + TypeTag::Struct(tag) => Balance::is_balance(tag), + _ => false, + } +} + +/// Rust version of the Move +/// stardust::timelocked_staked_iota::TimelockedStakedIota type. +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct TimelockedStakedIota { + id: UID, + /// A self-custodial object holding the staked IOTA tokens. + staked_iota: StakedIota, + /// This is the epoch time stamp of when the lock expires. + expiration_timestamp_ms: u64, + /// Timelock related label. + label: Option, +} + +impl TimelockedStakedIota { + /// Get the TimeLock's `type`. + pub fn type_() -> StructTag { + StructTag { + address: IOTA_SYSTEM_ADDRESS, + module: TIMELOCKED_STAKED_IOTA_MODULE_NAME.to_owned(), + name: TIMELOCKED_STAKED_IOTA_STRUCT_NAME.to_owned(), + type_params: vec![], + } + } + + /// Is this other StructTag representing a TimelockedStakedIota? + pub fn is_timelocked_staked_iota(s: &StructTag) -> bool { + s.address == IOTA_SYSTEM_ADDRESS + && s.module.as_ident_str() == TIMELOCKED_STAKED_IOTA_MODULE_NAME + && s.name.as_ident_str() == TIMELOCKED_STAKED_IOTA_STRUCT_NAME + && s.type_params.is_empty() + } + + /// Get the TimelockedStakedIota's `id`. + pub fn id(&self) -> ObjectID { + self.id.id.bytes + } + + /// Get the wrapped StakedIota's `pool_id`. + pub fn pool_id(&self) -> ObjectID { + self.staked_iota.pool_id() + } + + /// Get the wrapped StakedIota's `activation_epoch`. + pub fn activation_epoch(&self) -> EpochId { + self.staked_iota.activation_epoch() + } + + /// Get the wrapped StakedIota's `request_epoch`. + pub fn request_epoch(&self) -> EpochId { + // TODO: this might change when we implement warm up period. + self.staked_iota.activation_epoch().saturating_sub(1) + } + + /// Get the wrapped StakedIota's `principal`. + pub fn principal(&self) -> u64 { + self.staked_iota.principal() + } + + /// Get the TimelockedStakedIota's `expiration_timestamp_ms`. + pub fn expiration_timestamp_ms(&self) -> u64 { + self.expiration_timestamp_ms + } + + /// Get the TimelockedStakedIota's `label``. + pub fn label(&self) -> &Option { + &self.label + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs b/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs new file mode 100644 index 0000000000..fe819c04b6 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs @@ -0,0 +1,8 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::ident_str; +use super::super::super::move_core_types::identifier::IdentStr; + +pub const TIMELOCKED_STAKED_IOTA_MODULE_NAME: &IdentStr = ident_str!("timelocked_staking"); +pub const TIMELOCKED_STAKED_IOTA_STRUCT_NAME: &IdentStr = ident_str!("TimelockedStakedIota"); \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs b/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs new file mode 100644 index 0000000000..14d4f49a87 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs @@ -0,0 +1,166 @@ +// Copyright (c) 2021, Facebook, Inc. and its affiliates +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::{Display, Formatter}; + +use serde::{Deserialize, Serialize}; + +use super::super::move_core_types::identifier::Identifier; +use super::super::move_core_types::language_storage::TypeTag; + +use super::base_types::{ObjectID, ObjectRef, SequenceNumber}; +use super::{ + IOTA_CLOCK_OBJECT_ID, + IOTA_CLOCK_OBJECT_SHARED_VERSION, + IOTA_AUTHENTICATOR_STATE_OBJECT_ID, + IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION, + IOTA_SYSTEM_STATE_OBJECT_ID, + IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION, +}; + +pub const TEST_ONLY_GAS_UNIT_FOR_TRANSFER: u64 = 10_000; +pub const TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS: u64 = 50_000; +pub const TEST_ONLY_GAS_UNIT_FOR_PUBLISH: u64 = 70_000; +pub const TEST_ONLY_GAS_UNIT_FOR_STAKING: u64 = 50_000; +pub const TEST_ONLY_GAS_UNIT_FOR_GENERIC: u64 = 50_000; +pub const TEST_ONLY_GAS_UNIT_FOR_SPLIT_COIN: u64 = 10_000; +// For some transactions we may either perform heavy operations or touch +// objects that are storage expensive. That may happen (and often is the case) +// because the object touched are set up in genesis and carry no storage cost +// (and thus rebate) on first usage. +pub const TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE: u64 = 5_000_000; + +pub const GAS_PRICE_FOR_SYSTEM_TX: u64 = 1; + +pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = 1000; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub enum CallArg { + // contains no structs or objects + Pure(Vec), + // an object + Object(ObjectArg), +} + +impl CallArg { + pub const IOTA_SYSTEM_MUT: Self = Self::Object(ObjectArg::IOTA_SYSTEM_MUT); + pub const CLOCK_IMM: Self = Self::Object(ObjectArg::SharedObject { + id: IOTA_CLOCK_OBJECT_ID, + initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION, + mutable: false, + }); + pub const CLOCK_MUT: Self = Self::Object(ObjectArg::SharedObject { + id: IOTA_CLOCK_OBJECT_ID, + initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION, + mutable: true, + }); + pub const AUTHENTICATOR_MUT: Self = Self::Object(ObjectArg::SharedObject { + id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID, + initial_shared_version: IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION, + mutable: true, + }); +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)] +pub enum ObjectArg { + // A Move object, either immutable, or owned mutable. + ImmOrOwnedObject(ObjectRef), + // A Move object that's shared. + // SharedObject::mutable controls whether caller asks for a mutable reference to shared + // object. + SharedObject { + id: ObjectID, + initial_shared_version: SequenceNumber, + mutable: bool, + }, + // A Move object that can be received in this transaction. + Receiving(ObjectRef), +} + +impl ObjectArg { + pub const IOTA_SYSTEM_MUT: Self = Self::SharedObject { + id: IOTA_SYSTEM_STATE_OBJECT_ID, + initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION, + mutable: true, + }; +} + +/// The command for calling a Move function, either an entry function or a +/// public function (which cannot return references). +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct ProgrammableMoveCall { + /// The package containing the module and function. + pub package: ObjectID, + /// The specific module in the package containing the function. + pub module: Identifier, + /// The function to be called. + pub function: Identifier, + /// The type arguments to the function. + pub type_arguments: Vec, + /// The arguments to the function. + pub arguments: Vec, +} + +/// A single command in a programmable transaction. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub enum Command { + /// A call to either an entry or a public Move function + MoveCall(Box), + /// `(Vec, address)` + /// It sends n-objects to the specified address. These objects must have + /// store (public transfer) and either the previous owner must be an + /// address or the object must be newly created. + TransferObjects(Vec, Argument), + /// `(&mut Coin, Vec)` -> `Vec>` + /// It splits off some amounts into a new coins with those amounts + SplitCoins(Argument, Vec), + /// `(&mut Coin, Vec>)` + /// It merges n-coins into the first coin + MergeCoins(Argument, Vec), + /// Publishes a Move package. It takes the package bytes and a list of the + /// package's transitive dependencies to link against on-chain. + Publish(Vec>, Vec), + /// `forall T: Vec -> vector` + /// Given n-values of the same type, it constructs a vector. For non objects + /// or an empty vector, the type tag must be specified. + MakeMoveVec(Option, Vec), + /// Upgrades a Move package + /// Takes (in order): + /// 1. A vector of serialized modules for the package. + /// 2. A vector of object ids for the transitive dependencies of the new + /// package. + /// 3. The object ID of the package being upgraded. + /// 4. An argument holding the `UpgradeTicket` that must have been produced + /// from an earlier command in the same programmable transaction. + Upgrade(Vec>, Vec, ObjectID, Argument), +} + +/// An argument to a programmable transaction command +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)] +pub enum Argument { + /// The gas coin. The gas coin can only be used by-ref, except for with + /// `TransferObjects`, which can use it by-value. + GasCoin, + /// One of the input objects or primitive values (from + /// `ProgrammableTransaction` inputs) + Input(u16), + /// The result of another command (from `ProgrammableTransaction` commands) + Result(u16), + /// Like a `Result` but it accesses a nested result. Currently, the only + /// usage of this is to access a value from a Move call with multiple + /// return values. + NestedResult(u16, u16), +} + +impl Display for Argument { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Argument::GasCoin => write!(f, "GasCoin"), + Argument::Input(i) => write!(f, "Input({i})"), + Argument::Result(i) => write!(f, "Result({i})"), + Argument::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"), + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/mod.rs b/identity_iota_interaction/src/sdk_types/mod.rs new file mode 100644 index 0000000000..57d30805bc --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/mod.rs @@ -0,0 +1,18 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[path = "iota_json_rpc_types/mod.rs"] +pub mod rpc_types; +#[path = "iota_types/mod.rs"] +pub mod types; +#[path = "move_core_types/mod.rs"] +pub mod move_types; + +pub mod move_command_line_common; +pub mod shared_crypto; +pub mod error; +pub mod generated_types; + +pub(crate) use types as iota_types; +pub(crate) use move_types as move_core_types; +pub(crate) use rpc_types as iota_json_rpc_types; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_command_line_common/address.rs b/identity_iota_interaction/src/sdk_types/move_command_line_common/address.rs new file mode 100644 index 0000000000..4204f3e98d --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_command_line_common/address.rs @@ -0,0 +1,196 @@ +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{fmt, hash::Hash}; +use std::option::Option; +use std::option::Option::{Some, None}; +use std::string::String; + +use num_bigint::BigUint; +use anyhow::anyhow; + +use super::super::move_core_types::account_address::AccountAddress; +use super::parser::{NumberFormat, parse_address_number}; + +// Parsed Address, either a name or a numerical address +#[derive(Eq, PartialEq, Debug, Clone)] +pub enum ParsedAddress { + Named(String), + Numerical(NumericalAddress), +} + +/// Numerical address represents non-named address values +/// or the assigned value of a named address +#[derive(Clone, Copy)] +pub struct NumericalAddress { + /// the number for the address + bytes: AccountAddress, + /// The format (e.g. decimal or hex) for displaying the number + format: NumberFormat, +} + +impl ParsedAddress { + pub fn into_account_address( + self, + mapping: &impl Fn(&str) -> Option, + ) -> anyhow::Result { + match self { + Self::Named(n) => { + mapping(n.as_str()).ok_or_else(|| anyhow!("Unbound named address: '{}'", n)) + } + Self::Numerical(a) => Ok(a.into_inner()), + } + } +} + +impl NumericalAddress { + // bytes used for errors when an address is not known but is needed + pub const DEFAULT_ERROR_ADDRESS: Self = NumericalAddress { + bytes: AccountAddress::ONE, + format: NumberFormat::Hex, + }; + + pub const fn new(bytes: [u8; AccountAddress::LENGTH], format: NumberFormat) -> Self { + Self { + bytes: AccountAddress::new(bytes), + format, + } + } + + pub fn into_inner(self) -> AccountAddress { + self.bytes + } + + pub fn into_bytes(self) -> [u8; AccountAddress::LENGTH] { + self.bytes.into_bytes() + } + + pub fn parse_str(s: &str) -> Result { + match parse_address_number(s) { + Some((n, format)) => Ok(NumericalAddress { + bytes: AccountAddress::new(n), + format, + }), + None => + // TODO the kind of error is in an unstable nightly API + // But currently the only way this should fail is if the number is too long + { + Err(format!( + "Invalid address literal. The numeric value is too large. \ + The maximum size is {} bytes", + AccountAddress::LENGTH, + )) + } + } + } +} + +impl AsRef<[u8]> for NumericalAddress { + fn as_ref(&self) -> &[u8] { + self.bytes.as_ref() + } +} + +impl fmt::Display for NumericalAddress { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.format { + NumberFormat::Decimal => { + let n = BigUint::from_bytes_be(self.bytes.as_ref()); + write!(f, "{}", n) + } + NumberFormat::Hex => write!(f, "{:#X}", self), + } + } +} + +impl fmt::Debug for NumericalAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::UpperHex for NumericalAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let encoded = hex::encode_upper(self.as_ref()); + let dropped = encoded + .chars() + .skip_while(|c| c == &'0') + .collect::(); + let prefix = if f.alternate() { "0x" } else { "" }; + if dropped.is_empty() { + write!(f, "{}0", prefix) + } else { + write!(f, "{}{}", prefix, dropped) + } + } +} + +impl fmt::LowerHex for NumericalAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let encoded = hex::encode(self.as_ref()); + let dropped = encoded + .chars() + .skip_while(|c| c == &'0') + .collect::(); + let prefix = if f.alternate() { "0x" } else { "" }; + if dropped.is_empty() { + write!(f, "{}0", prefix) + } else { + write!(f, "{}{}", prefix, dropped) + } + } +} + +impl fmt::Display for ParsedAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Named(n) => write!(f, "{n}"), + Self::Numerical(a) => write!(f, "{a}"), + } + } +} + +impl PartialOrd for NumericalAddress { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for NumericalAddress { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let Self { + bytes: self_bytes, + format: _, + } = self; + let Self { + bytes: other_bytes, + format: _, + } = other; + self_bytes.cmp(other_bytes) + } +} + +impl PartialEq for NumericalAddress { + fn eq(&self, other: &Self) -> bool { + let Self { + bytes: self_bytes, + format: _, + } = self; + let Self { + bytes: other_bytes, + format: _, + } = other; + self_bytes == other_bytes + } +} +impl Eq for NumericalAddress {} + +impl Hash for NumericalAddress { + fn hash(&self, state: &mut H) { + let Self { + bytes: self_bytes, + format: _, + } = self; + self_bytes.hash(state) + } +} diff --git a/identity_iota_interaction/src/sdk_types/move_command_line_common/mod.rs b/identity_iota_interaction/src/sdk_types/move_command_line_common/mod.rs new file mode 100644 index 0000000000..6dffbb0edb --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_command_line_common/mod.rs @@ -0,0 +1,7 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub mod address; +pub mod parser; +pub mod values; +pub mod types; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_command_line_common/parser.rs b/identity_iota_interaction/src/sdk_types/move_command_line_common/parser.rs new file mode 100644 index 0000000000..2cd61c268f --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_command_line_common/parser.rs @@ -0,0 +1,680 @@ +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{fmt::Display, iter::Peekable, num::ParseIntError}; + +use anyhow::{anyhow, bail, Result}; +use super::super::{ + move_core_types::{ + account_address::AccountAddress, + u256::{U256FromStrError, U256}, + }, +}; +use num_bigint::BigUint; + +use super::{ + address::{NumericalAddress, ParsedAddress}, + types::{ParsedFqName, ParsedModuleId, ParsedStructType, ParsedType, TypeToken}, + values::{ParsableValue, ParsedValue, ValueToken}, +}; + +const MAX_TYPE_DEPTH: u64 = 128; +const MAX_TYPE_NODE_COUNT: u64 = 256; + +pub trait Token: Display + Copy + Eq { + fn is_whitespace(&self) -> bool; + fn next_token(s: &str) -> Result>; + fn tokenize(mut s: &str) -> Result> { + let mut v = vec![]; + while let Some((tok, n)) = Self::next_token(s)? { + v.push((tok, &s[..n])); + s = &s[n..]; + } + Ok(v) + } +} + +pub struct Parser<'a, Tok: Token, I: Iterator> { + count: u64, + it: Peekable, +} + +impl ParsedType { + pub fn parse(s: &str) -> Result { + parse(s, |parser| parser.parse_type()) + } +} + +impl ParsedModuleId { + pub fn parse(s: &str) -> Result { + parse(s, |parser| parser.parse_module_id()) + } +} + +impl ParsedFqName { + pub fn parse(s: &str) -> Result { + parse(s, |parser| parser.parse_fq_name()) + } +} + +impl ParsedStructType { + pub fn parse(s: &str) -> Result { + let ty = parse(s, |parser| parser.parse_type()) + .map_err(|e| anyhow!("Invalid struct type: {}. Got error: {}", s, e))?; + match ty { + ParsedType::Struct(s) => Ok(s), + _ => bail!("Invalid struct type: {}", s), + } + } +} + +impl ParsedAddress { + pub fn parse(s: &str) -> Result { + parse(s, |parser| parser.parse_address()) + } +} + +impl ParsedValue { + pub fn parse(s: &str) -> Result> { + parse(s, |parser| parser.parse_value()) + } +} + +fn parse<'a, Tok: Token, R>( + s: &'a str, + f: impl FnOnce(&mut Parser<'a, Tok, std::vec::IntoIter<(Tok, &'a str)>>) -> Result, +) -> Result { + let tokens: Vec<_> = Tok::tokenize(s)? + .into_iter() + .filter(|(tok, _)| !tok.is_whitespace()) + .collect(); + let mut parser = Parser::new(tokens); + let res = f(&mut parser)?; + if let Ok((_, contents)) = parser.advance_any() { + bail!("Expected end of token stream. Got: {}", contents) + } + Ok(res) +} + +impl<'a, Tok: Token, I: Iterator> Parser<'a, Tok, I> { + pub fn new>(v: T) -> Self { + Self { + count: 0, + it: v.into_iter().peekable(), + } + } + + pub fn advance_any(&mut self) -> Result<(Tok, &'a str)> { + match self.it.next() { + Some(tok) => Ok(tok), + None => bail!("unexpected end of tokens"), + } + } + + pub fn advance(&mut self, expected_token: Tok) -> Result<&'a str> { + let (t, contents) = self.advance_any()?; + if t != expected_token { + bail!("expected token {}, got {}", expected_token, t) + } + Ok(contents) + } + + pub fn peek(&mut self) -> Option<(Tok, &'a str)> { + self.it.peek().copied() + } + + pub fn peek_tok(&mut self) -> Option { + self.it.peek().map(|(tok, _)| *tok) + } + + pub fn parse_list( + &mut self, + parse_list_item: impl Fn(&mut Self) -> Result, + delim: Tok, + end_token: Tok, + allow_trailing_delim: bool, + ) -> Result> { + let is_end = + |tok_opt: Option| -> bool { tok_opt.map(|tok| tok == end_token).unwrap_or(true) }; + let mut v = vec![]; + while !is_end(self.peek_tok()) { + v.push(parse_list_item(self)?); + if is_end(self.peek_tok()) { + break; + } + self.advance(delim)?; + if is_end(self.peek_tok()) && allow_trailing_delim { + break; + } + } + Ok(v) + } +} + +impl<'a, I: Iterator> Parser<'a, TypeToken, I> { + pub fn parse_module_id(&mut self) -> Result { + let (tok, contents) = self.advance_any()?; + self.parse_module_id_impl(tok, contents) + } + + pub fn parse_fq_name(&mut self) -> Result { + let (tok, contents) = self.advance_any()?; + self.parse_fq_name_impl(tok, contents) + } + + pub fn parse_type(&mut self) -> Result { + self.parse_type_impl(0) + } + + pub fn parse_module_id_impl( + &mut self, + tok: TypeToken, + contents: &'a str, + ) -> Result { + let tok = match tok { + TypeToken::Ident => ValueToken::Ident, + TypeToken::AddressIdent => ValueToken::Number, + tok => bail!("unexpected token {tok}, expected address"), + }; + let address = parse_address_impl(tok, contents)?; + self.advance(TypeToken::ColonColon)?; + let name = self.advance(TypeToken::Ident)?.to_owned(); + Ok(ParsedModuleId { address, name }) + } + + pub fn parse_fq_name_impl( + &mut self, + tok: TypeToken, + contents: &'a str, + ) -> Result { + let module = self.parse_module_id_impl(tok, contents)?; + self.advance(TypeToken::ColonColon)?; + let name = self.advance(TypeToken::Ident)?.to_owned(); + Ok(ParsedFqName { module, name }) + } + + fn parse_type_impl(&mut self, depth: u64) -> Result { + self.count += 1; + + if depth > MAX_TYPE_DEPTH || self.count > MAX_TYPE_NODE_COUNT { + bail!("Type exceeds maximum nesting depth or node count") + } + + Ok(match self.advance_any()? { + (TypeToken::Ident, "u8") => ParsedType::U8, + (TypeToken::Ident, "u16") => ParsedType::U16, + (TypeToken::Ident, "u32") => ParsedType::U32, + (TypeToken::Ident, "u64") => ParsedType::U64, + (TypeToken::Ident, "u128") => ParsedType::U128, + (TypeToken::Ident, "u256") => ParsedType::U256, + (TypeToken::Ident, "bool") => ParsedType::Bool, + (TypeToken::Ident, "address") => ParsedType::Address, + (TypeToken::Ident, "signer") => ParsedType::Signer, + (TypeToken::Ident, "vector") => { + self.advance(TypeToken::Lt)?; + let ty = self.parse_type_impl(depth + 1)?; + self.advance(TypeToken::Gt)?; + ParsedType::Vector(Box::new(ty)) + } + + (tok @ (TypeToken::Ident | TypeToken::AddressIdent), contents) => { + let fq_name = self.parse_fq_name_impl(tok, contents)?; + let type_args = match self.peek_tok() { + Some(TypeToken::Lt) => { + self.advance(TypeToken::Lt)?; + let type_args = self.parse_list( + |parser| parser.parse_type_impl(depth + 1), + TypeToken::Comma, + TypeToken::Gt, + true, + )?; + self.advance(TypeToken::Gt)?; + type_args + } + _ => vec![], + }; + ParsedType::Struct(ParsedStructType { fq_name, type_args }) + } + (tok, _) => bail!("unexpected token {tok}, expected type"), + }) + } +} + +impl<'a, I: Iterator> Parser<'a, ValueToken, I> { + pub fn parse_value(&mut self) -> Result> { + if let Some(extra) = Extra::parse_value(self) { + return Ok(ParsedValue::Custom(extra?)); + } + let (tok, contents) = self.advance_any()?; + Ok(match tok { + ValueToken::Number if !matches!(self.peek_tok(), Some(ValueToken::ColonColon)) => { + let (u, _) = parse_u256(contents)?; + ParsedValue::InferredNum(u) + } + ValueToken::NumberTyped => { + if let Some(s) = contents.strip_suffix("u8") { + let (u, _) = parse_u8(s)?; + ParsedValue::U8(u) + } else if let Some(s) = contents.strip_suffix("u16") { + let (u, _) = parse_u16(s)?; + ParsedValue::U16(u) + } else if let Some(s) = contents.strip_suffix("u32") { + let (u, _) = parse_u32(s)?; + ParsedValue::U32(u) + } else if let Some(s) = contents.strip_suffix("u64") { + let (u, _) = parse_u64(s)?; + ParsedValue::U64(u) + } else if let Some(s) = contents.strip_suffix("u128") { + let (u, _) = parse_u128(s)?; + ParsedValue::U128(u) + } else { + let (u, _) = parse_u256(contents.strip_suffix("u256").unwrap())?; + ParsedValue::U256(u) + } + } + ValueToken::True => ParsedValue::Bool(true), + ValueToken::False => ParsedValue::Bool(false), + + ValueToken::ByteString => { + let contents = contents + .strip_prefix("b\"") + .unwrap() + .strip_suffix('\"') + .unwrap(); + ParsedValue::Vector( + contents + .as_bytes() + .iter() + .copied() + .map(ParsedValue::U8) + .collect(), + ) + } + ValueToken::HexString => { + let contents = contents + .strip_prefix("x\"") + .unwrap() + .strip_suffix('\"') + .unwrap() + .to_ascii_lowercase(); + ParsedValue::Vector( + hex::decode(contents) + .unwrap() + .into_iter() + .map(ParsedValue::U8) + .collect(), + ) + } + ValueToken::Utf8String => { + let contents = contents + .strip_prefix('\"') + .unwrap() + .strip_suffix('\"') + .unwrap(); + ParsedValue::Vector( + contents + .as_bytes() + .iter() + .copied() + .map(ParsedValue::U8) + .collect(), + ) + } + + ValueToken::AtSign => ParsedValue::Address(self.parse_address()?), + + ValueToken::Ident if contents == "vector" => { + self.advance(ValueToken::LBracket)?; + let values = self.parse_list( + |parser| parser.parse_value(), + ValueToken::Comma, + ValueToken::RBracket, + true, + )?; + self.advance(ValueToken::RBracket)?; + ParsedValue::Vector(values) + } + + ValueToken::Ident if contents == "struct" => { + self.advance(ValueToken::LParen)?; + let values = self.parse_list( + |parser| parser.parse_value(), + ValueToken::Comma, + ValueToken::RParen, + true, + )?; + self.advance(ValueToken::RParen)?; + ParsedValue::Struct(values) + } + + _ => bail!("unexpected token {}, expected type", tok), + }) + } + + pub fn parse_address(&mut self) -> Result { + let (tok, contents) = self.advance_any()?; + parse_address_impl(tok, contents) + } +} + +pub fn parse_address_impl(tok: ValueToken, contents: &str) -> Result { + Ok(match tok { + ValueToken::Number => { + ParsedAddress::Numerical(NumericalAddress::parse_str(contents).map_err(|s| { + anyhow!( + "Failed to parse numerical address '{}'. Got error: {}", + contents, + s + ) + })?) + } + ValueToken::Ident => ParsedAddress::Named(contents.to_owned()), + _ => bail!("unexpected token {}, expected identifier or number", tok), + }) +} + +#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)] +#[repr(u32)] +/// Number format enum, the u32 value represents the base +pub enum NumberFormat { + Decimal = 10, + Hex = 16, +} + +// Determines the base of the number literal, depending on the prefix +pub(crate) fn determine_num_text_and_base(s: &str) -> (&str, NumberFormat) { + match s.strip_prefix("0x") { + Some(s_hex) => (s_hex, NumberFormat::Hex), + None => (s, NumberFormat::Decimal), + } +} + +// Parse a u8 from a decimal or hex encoding +pub fn parse_u8(s: &str) -> Result<(u8, NumberFormat), ParseIntError> { + let (txt, base) = determine_num_text_and_base(s); + Ok(( + u8::from_str_radix(&txt.replace('_', ""), base as u32)?, + base, + )) +} + +// Parse a u16 from a decimal or hex encoding +pub fn parse_u16(s: &str) -> Result<(u16, NumberFormat), ParseIntError> { + let (txt, base) = determine_num_text_and_base(s); + Ok(( + u16::from_str_radix(&txt.replace('_', ""), base as u32)?, + base, + )) +} + +// Parse a u32 from a decimal or hex encoding +pub fn parse_u32(s: &str) -> Result<(u32, NumberFormat), ParseIntError> { + let (txt, base) = determine_num_text_and_base(s); + Ok(( + u32::from_str_radix(&txt.replace('_', ""), base as u32)?, + base, + )) +} + +// Parse a u64 from a decimal or hex encoding +pub fn parse_u64(s: &str) -> Result<(u64, NumberFormat), ParseIntError> { + let (txt, base) = determine_num_text_and_base(s); + Ok(( + u64::from_str_radix(&txt.replace('_', ""), base as u32)?, + base, + )) +} + +// Parse a u128 from a decimal or hex encoding +pub fn parse_u128(s: &str) -> Result<(u128, NumberFormat), ParseIntError> { + let (txt, base) = determine_num_text_and_base(s); + Ok(( + u128::from_str_radix(&txt.replace('_', ""), base as u32)?, + base, + )) +} + +// Parse a u256 from a decimal or hex encoding +pub fn parse_u256(s: &str) -> Result<(U256, NumberFormat), U256FromStrError> { + let (txt, base) = determine_num_text_and_base(s); + Ok(( + U256::from_str_radix(&txt.replace('_', ""), base as u32)?, + base, + )) +} + +// Parse an address from a decimal or hex encoding +pub fn parse_address_number(s: &str) -> Option<([u8; AccountAddress::LENGTH], NumberFormat)> { + let (txt, base) = determine_num_text_and_base(s); + let parsed = BigUint::parse_bytes( + txt.as_bytes(), + match base { + NumberFormat::Hex => 16, + NumberFormat::Decimal => 10, + }, + )?; + let bytes = parsed.to_bytes_be(); + if bytes.len() > AccountAddress::LENGTH { + return None; + } + let mut result = [0u8; AccountAddress::LENGTH]; + result[(AccountAddress::LENGTH - bytes.len())..].clone_from_slice(&bytes); + Some((result, base)) +} + +#[cfg(test)] +mod tests { + use move_core_types::{account_address::AccountAddress, u256::U256}; + + use crate::{ + address::{NumericalAddress, ParsedAddress}, + types::{ParsedStructType, ParsedType}, + values::ParsedValue, + }; + + #[allow(clippy::unreadable_literal)] + #[test] + fn tests_parse_value_positive() { + use ParsedValue as V; + let cases: &[(&str, V)] = &[ + (" 0u8", V::U8(0)), + ("0u8", V::U8(0)), + ("0xF_Fu8", V::U8(255)), + ("0xF__FF__Eu16", V::U16(u16::MAX - 1)), + ("0xFFF_FF__FF_Cu32", V::U32(u32::MAX - 3)), + ("255u8", V::U8(255)), + ("255u256", V::U256(U256::from(255u64))), + ("0", V::InferredNum(U256::from(0u64))), + ("0123", V::InferredNum(U256::from(123u64))), + ("0xFF", V::InferredNum(U256::from(0xFFu64))), + ("0xF_F", V::InferredNum(U256::from(0xFFu64))), + ("0xFF__", V::InferredNum(U256::from(0xFFu64))), + ( + "0x12_34__ABCD_FF", + V::InferredNum(U256::from(0x1234ABCDFFu64)), + ), + ("0u64", V::U64(0)), + ("0x0u64", V::U64(0)), + ( + "18446744073709551615", + V::InferredNum(U256::from(18446744073709551615u128)), + ), + ("18446744073709551615u64", V::U64(18446744073709551615)), + ("0u128", V::U128(0)), + ("1_0u8", V::U8(1_0)), + ("10_u8", V::U8(10)), + ("1_000u64", V::U64(1_000)), + ("1_000", V::InferredNum(U256::from(1_000u32))), + ("1_0_0_0u64", V::U64(1_000)), + ("1_000_000u128", V::U128(1_000_000)), + ( + "340282366920938463463374607431768211455u128", + V::U128(340282366920938463463374607431768211455), + ), + ("true", V::Bool(true)), + ("false", V::Bool(false)), + ( + "@0x0", + V::Address(ParsedAddress::Numerical(NumericalAddress::new( + AccountAddress::from_hex_literal("0x0") + .unwrap() + .into_bytes(), + crate::parser::NumberFormat::Hex, + ))), + ), + ( + "@0", + V::Address(ParsedAddress::Numerical(NumericalAddress::new( + AccountAddress::from_hex_literal("0x0") + .unwrap() + .into_bytes(), + crate::parser::NumberFormat::Hex, + ))), + ), + ( + "@0x54afa3526", + V::Address(ParsedAddress::Numerical(NumericalAddress::new( + AccountAddress::from_hex_literal("0x54afa3526") + .unwrap() + .into_bytes(), + crate::parser::NumberFormat::Hex, + ))), + ), + ( + "b\"hello\"", + V::Vector("hello".as_bytes().iter().copied().map(V::U8).collect()), + ), + ("x\"7fff\"", V::Vector(vec![V::U8(0x7f), V::U8(0xff)])), + ("x\"\"", V::Vector(vec![])), + ("x\"00\"", V::Vector(vec![V::U8(0x00)])), + ( + "x\"deadbeef\"", + V::Vector(vec![V::U8(0xde), V::U8(0xad), V::U8(0xbe), V::U8(0xef)]), + ), + ]; + + for (s, expected) in cases { + assert_eq!(&ParsedValue::parse(s).unwrap(), expected) + } + } + + #[test] + fn tests_parse_value_negative() { + /// Test cases for the parser that should always fail. + const PARSE_VALUE_NEGATIVE_TEST_CASES: &[&str] = &[ + "-3", + "0u42", + "0u645", + "0u64x", + "0u6 4", + "0u", + "_10", + "_10_u8", + "_10__u8", + "10_u8__", + "0xFF_u8_", + "0xF_u8__", + "0x_F_u8__", + "_", + "__", + "__4", + "_u8", + "5_bool", + "256u8", + "4294967296u32", + "65536u16", + "18446744073709551616u64", + "340282366920938463463374607431768211456u128", + "340282366920938463463374607431768211456340282366920938463463374607431768211456340282366920938463463374607431768211456340282366920938463463374607431768211456u256", + "0xg", + "0x00g0", + "0x", + "0x_", + "", + "@@", + "()", + "x\"ffff", + "x\"a \"", + "x\" \"", + "x\"0g\"", + "x\"0\"", + "garbage", + "true3", + "3false", + "3 false", + "", + "0XFF", + "0X0", + ]; + + for s in PARSE_VALUE_NEGATIVE_TEST_CASES { + assert!( + ParsedValue::<()>::parse(s).is_err(), + "Unexpectedly succeeded in parsing: {}", + s + ) + } + } + + #[test] + fn test_type_type() { + for s in &[ + "u64", + "bool", + "vector", + "vector>", + "address", + "signer", + "0x1::M::S", + "0x2::M::S_", + "0x3::M_::S", + "0x4::M_::S_", + "0x00000000004::M::S", + "0x1::M::S", + "0x1::M::S<0x2::P::Q>", + "vector<0x1::M::S>", + "vector<0x1::M_::S_>", + "vector>", + "0x1::M::S>", + ] { + assert!(ParsedType::parse(s).is_ok(), "Failed to parse type {}", s); + } + } + + #[test] + fn test_parse_valid_struct_type() { + let valid = vec![ + "0x1::Foo::Foo", + "0x1::Foo_Type::Foo", + "0x1::Foo_::Foo", + "0x1::X_123::X32_", + "0x1::Foo::Foo_Type", + "0x1::Foo::Foo<0x1::ABC::ABC>", + "0x1::Foo::Foo<0x1::ABC::ABC_Type>", + "0x1::Foo::Foo", + "0x1::Foo::Foo", + "0x1::Foo::Foo", + "0x1::Foo::Foo", + "0x1::Foo::Foo", + "0x1::Foo::Foo", + "0x1::Foo::Foo", + "0x1::Foo::Foo

", + "0x1::Foo::Foo", + "0x1::Foo::Foo>", + "0x1::Foo::Foo", + "0x1::Foo::Foo", + "0x1::Foo::Foo", + "0x1::Foo::Foo,address,signer>", + "0x1::Foo::Foo>>", + "0x1::Foo::Foo<0x1::Foo::Struct, 0x1::Foo::Foo>>>>", + ]; + for s in valid { + assert!( + ParsedStructType::parse(s).is_ok(), + "Failed to parse struct {}", + s + ); + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_command_line_common/types.rs b/identity_iota_interaction/src/sdk_types/move_command_line_common/types.rs new file mode 100644 index 0000000000..f2f09fd2ba --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_command_line_common/types.rs @@ -0,0 +1,188 @@ +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::{self, Display}; + +use anyhow::{bail}; +use super::super::move_core_types::{ + account_address::AccountAddress, + identifier::{Identifier, is_valid_identifier_char}, + language_storage::{StructTag, TypeTag, ModuleId} +}; + +use super::{address::ParsedAddress, parser::Token}; + +#[derive(Eq, PartialEq, Debug, Clone, Copy)] +pub enum TypeToken { + Whitespace, + Ident, + AddressIdent, + ColonColon, + Lt, + Gt, + Comma, +} + +#[derive(Eq, PartialEq, Debug, Clone)] +pub struct ParsedModuleId { + pub address: ParsedAddress, + pub name: String, +} + +#[derive(Eq, PartialEq, Debug, Clone)] +pub struct ParsedFqName { + pub module: ParsedModuleId, + pub name: String, +} + +#[derive(Eq, PartialEq, Debug, Clone)] +pub struct ParsedStructType { + pub fq_name: ParsedFqName, + pub type_args: Vec, +} + +#[derive(Eq, PartialEq, Debug, Clone)] +pub enum ParsedType { + U8, + U16, + U32, + U64, + U128, + U256, + Bool, + Address, + Signer, + Vector(Box), + Struct(ParsedStructType), +} + +impl Display for TypeToken { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let s = match *self { + TypeToken::Whitespace => "[whitespace]", + TypeToken::Ident => "[identifier]", + TypeToken::AddressIdent => "[address]", + TypeToken::ColonColon => "::", + TypeToken::Lt => "<", + TypeToken::Gt => ">", + TypeToken::Comma => ",", + }; + fmt::Display::fmt(s, formatter) + } +} + +impl Token for TypeToken { + fn is_whitespace(&self) -> bool { + matches!(self, Self::Whitespace) + } + + fn next_token(s: &str) -> anyhow::Result> { + let mut chars = s.chars().peekable(); + + let c = match chars.next() { + None => return Ok(None), + Some(c) => c, + }; + Ok(Some(match c { + '<' => (Self::Lt, 1), + '>' => (Self::Gt, 1), + ',' => (Self::Comma, 1), + ':' => match chars.next() { + Some(':') => (Self::ColonColon, 2), + _ => bail!("unrecognized token: {}", s), + }, + '0' if matches!(chars.peek(), Some('x') | Some('X')) => { + chars.next().unwrap(); + match chars.next() { + Some(c) if c.is_ascii_hexdigit() || c == '_' => { + // 0x + c + remaining + let len = 3 + chars + .take_while(|q| char::is_ascii_hexdigit(q) || *q == '_') + .count(); + (Self::AddressIdent, len) + } + _ => bail!("unrecognized token: {}", s), + } + } + c if c.is_ascii_digit() => { + // c + remaining + let len = 1 + chars.take_while(char::is_ascii_digit).count(); + (Self::AddressIdent, len) + } + c if c.is_ascii_whitespace() => { + // c + remaining + let len = 1 + chars.take_while(char::is_ascii_whitespace).count(); + (Self::Whitespace, len) + } + c if c.is_ascii_alphabetic() => { + // c + remaining + let len = 1 + chars + .take_while(|c| is_valid_identifier_char(*c)) + .count(); + (Self::Ident, len) + } + _ => bail!("unrecognized token: {}", s), + })) + } +} + +impl ParsedModuleId { + pub fn into_module_id( + self, + mapping: &impl Fn(&str) -> Option, + ) -> anyhow::Result { + Ok(ModuleId::new( + self.address.into_account_address(mapping)?, + Identifier::new(self.name)?, + )) + } +} + +impl ParsedFqName { + pub fn into_fq_name( + self, + mapping: &impl Fn(&str) -> Option, + ) -> anyhow::Result<(ModuleId, String)> { + Ok((self.module.into_module_id(mapping)?, self.name)) + } +} + +impl ParsedStructType { + pub fn into_struct_tag( + self, + mapping: &impl Fn(&str) -> Option, + ) -> anyhow::Result { + let Self { fq_name, type_args } = self; + Ok(StructTag { + address: fq_name.module.address.into_account_address(mapping)?, + module: Identifier::new(fq_name.module.name)?, + name: Identifier::new(fq_name.name)?, + type_params: type_args + .into_iter() + .map(|t| t.into_type_tag(mapping)) + .collect::>()?, + }) + } +} + +impl ParsedType { + pub fn into_type_tag( + self, + mapping: &impl Fn(&str) -> Option, + ) -> anyhow::Result { + Ok(match self { + ParsedType::U8 => TypeTag::U8, + ParsedType::U16 => TypeTag::U16, + ParsedType::U32 => TypeTag::U32, + ParsedType::U64 => TypeTag::U64, + ParsedType::U128 => TypeTag::U128, + ParsedType::U256 => TypeTag::U256, + ParsedType::Bool => TypeTag::Bool, + ParsedType::Address => TypeTag::Address, + ParsedType::Signer => TypeTag::Signer, + ParsedType::Vector(inner) => TypeTag::Vector(Box::new(inner.into_type_tag(mapping)?)), + ParsedType::Struct(s) => TypeTag::Struct(Box::new(s.into_struct_tag(mapping)?)), + }) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_command_line_common/values.rs b/identity_iota_interaction/src/sdk_types/move_command_line_common/values.rs new file mode 100644 index 0000000000..11650c5a8a --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_command_line_common/values.rs @@ -0,0 +1,337 @@ +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::{self, Display}; + +use anyhow::bail; + +use super::super::move_core_types::{ + account_address::AccountAddress, + identifier::{is_valid_identifier_char}, + u256::U256, +}; + +use super::{ + address::ParsedAddress, + parser::{Parser, Token}, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct MoveStruct(pub Vec); + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum MoveValue { + U8(u8), + U64(u64), + U128(u128), + Bool(bool), + Address(AccountAddress), + Vector(Vec), + Struct(MoveStruct), + Signer(AccountAddress), + // NOTE: Added in bytecode version v6, do not reorder! + U16(u16), + U32(u32), + U256(U256), +} + +#[derive(Eq, PartialEq, Debug, Clone, Copy)] +pub enum ValueToken { + Number, + NumberTyped, + True, + False, + ByteString, + HexString, + Utf8String, + Ident, + AtSign, + LBrace, + RBrace, + LBracket, + RBracket, + LParen, + RParen, + Comma, + Colon, + ColonColon, + Whitespace, +} + +#[derive(Eq, PartialEq, Debug, Clone)] +pub enum ParsedValue { + Address(ParsedAddress), + InferredNum(U256), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + U128(u128), + U256(U256), + Bool(bool), + Vector(Vec>), + Struct(Vec>), + Custom(Extra), +} +pub trait ParsableValue: Sized + Send + Sync + Clone + 'static { + type ConcreteValue: Send; + fn parse_value<'a, I: Iterator>( + parser: &mut Parser<'a, ValueToken, I>, + ) -> Option>; + + fn move_value_into_concrete(v: MoveValue) -> anyhow::Result; + fn concrete_vector(elems: Vec) -> anyhow::Result; + fn concrete_struct(values: Vec) -> anyhow::Result; + fn into_concrete_value( + self, + mapping: &impl Fn(&str) -> Option, + ) -> anyhow::Result; +} + +impl ParsableValue for () { + type ConcreteValue = MoveValue; + fn parse_value<'a, I: Iterator>( + _: &mut Parser<'a, ValueToken, I>, + ) -> Option> { + None + } + fn move_value_into_concrete(v: MoveValue) -> anyhow::Result { + Ok(v) + } + + fn concrete_vector(elems: Vec) -> anyhow::Result { + Ok(MoveValue::Vector(elems)) + } + + fn concrete_struct(values: Vec) -> anyhow::Result { + Ok(MoveValue::Struct(MoveStruct(values))) + } + fn into_concrete_value( + self, + _mapping: &impl Fn(&str) -> Option, + ) -> anyhow::Result { + unreachable!() + } +} + +impl Display for ValueToken { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let s = match self { + ValueToken::Number => "[num]", + ValueToken::NumberTyped => "[num typed]", + ValueToken::True => "true", + ValueToken::False => "false", + ValueToken::ByteString => "[byte string]", + ValueToken::Utf8String => "[utf8 string]", + ValueToken::HexString => "[hex string]", + ValueToken::Whitespace => "[whitespace]", + ValueToken::Ident => "[identifier]", + ValueToken::AtSign => "@", + ValueToken::LBrace => "{", + ValueToken::RBrace => "}", + ValueToken::LBracket => "[", + ValueToken::RBracket => "]", + ValueToken::LParen => "(", + ValueToken::RParen => ")", + ValueToken::Comma => ",", + ValueToken::Colon => ":", + ValueToken::ColonColon => "::", + }; + fmt::Display::fmt(s, formatter) + } +} + +impl Token for ValueToken { + fn is_whitespace(&self) -> bool { + matches!(self, Self::Whitespace) + } + + fn next_token(s: &str) -> anyhow::Result> { + fn number_maybe_with_suffix(text: &str, num_text_len: usize) -> (ValueToken, usize) { + let rest = &text[num_text_len..]; + if rest.starts_with("u8") { + (ValueToken::NumberTyped, num_text_len + 2) + } else if rest.starts_with("u64") || rest.starts_with("u16") || rest.starts_with("u32") + { + (ValueToken::NumberTyped, num_text_len + 3) + } else if rest.starts_with("u128") || rest.starts_with("u256") { + (ValueToken::NumberTyped, num_text_len + 4) + } else { + // No typed suffix + (ValueToken::Number, num_text_len) + } + } + if s.starts_with("true") { + return Ok(Some((Self::True, 4))); + } + if s.starts_with("false") { + return Ok(Some((Self::False, 5))); + } + + let mut chars = s.chars().peekable(); + let c = match chars.next() { + None => return Ok(None), + Some(c) => c, + }; + Ok(Some(match c { + '@' => (Self::AtSign, 1), + '{' => (Self::LBrace, 1), + '}' => (Self::RBrace, 1), + '[' => (Self::LBracket, 1), + ']' => (Self::RBracket, 1), + '(' => (Self::LParen, 1), + ')' => (Self::RParen, 1), + ',' => (Self::Comma, 1), + ':' if matches!(chars.peek(), Some(':')) => (Self::ColonColon, 2), + ':' => (Self::Colon, 1), + '0' if matches!(chars.peek(), Some('x')) => { + chars.next().unwrap(); + match chars.next() { + Some(c) if c.is_ascii_hexdigit() => { + let len = 3 + chars + .take_while(|c| char::is_ascii_hexdigit(c) || *c == '_') + .count(); + number_maybe_with_suffix(s, len) + } + _ => bail!("unrecognized token: {}", s), + } + } + 'b' if matches!(chars.peek(), Some('"')) => { + chars.next().unwrap(); + // b" + let mut len = 2; + loop { + len += 1; + match chars.next() { + Some('"') => break, + Some(c) if c.is_ascii() => (), + Some(c) => bail!( + "Unexpected non-ascii character '{}' in byte string: {}", + c.escape_default(), + s + ), + None => bail!("Unexpected end of string before end quote: {}", s), + } + } + if s[..len].chars().any(|c| c == '\\') { + bail!( + "Escape characters not yet supported in byte string: {}", + &s[..len] + ) + } + (ValueToken::ByteString, len) + } + 'x' if matches!(chars.peek(), Some('"')) => { + chars.next().unwrap(); + // x" + let mut len = 2; + loop { + len += 1; + match chars.next() { + Some('"') => break, + Some(c) if c.is_ascii_hexdigit() => (), + Some(c) => bail!( + "Unexpected non-hexdigit '{}' in hex string: {}", + c.escape_default(), + s + ), + None => bail!("Unexpected end of string before end quote: {}", s), + } + } + assert!(len >= 3); + let num_digits = len - 3; + if num_digits % 2 != 0 { + bail!( + "Expected an even number of hex digits in hex string: {}", + &s[..len] + ) + } + (ValueToken::HexString, len) + } + '"' => { + // there is no need to check if a given char is valid UTF8 as it is already + // guaranteed; from the Rust docs + // (https://doc.rust-lang.org/std/primitive.char.html): "char values are USVs and + // str values are valid UTF-8, it is safe to store any char in a str or read any + // character from a str as a char"; this means that while not every char is + // valid UTF8, those stored in &str are + let end_quote_byte_offset = match s[1..].find('"') { + Some(o) => o, + None => bail!("Unexpected end of string before end quote: {}", s), + }; + // the length of the token (which we need in bytes rather than chars as s is + // sliced in parser and slicing str uses byte indexes) is the + // same as position of the ending double quote (in the whole + // string) plus 1 + let len = s[..1].len() + end_quote_byte_offset + 1; + if s[..len].chars().any(|c| c == '\\') { + bail!( + "Escape characters not yet supported in utf8 string: {}", + &s[..len] + ) + } + (ValueToken::Utf8String, len) + } + c if c.is_ascii_digit() => { + // c + remaining + let len = 1 + chars + .take_while(|c| char::is_ascii_digit(c) || *c == '_') + .count(); + number_maybe_with_suffix(s, len) + } + c if c.is_ascii_whitespace() => { + // c + remaining + let len = 1 + chars.take_while(char::is_ascii_whitespace).count(); + (Self::Whitespace, len) + } + c if c.is_ascii_alphabetic() => { + // c + remaining + // TODO be more permissive + let len = 1 + chars + .take_while(|c| is_valid_identifier_char(*c)) + .count(); + (Self::Ident, len) + } + _ => bail!("unrecognized token: {}", s), + })) + } +} + +impl ParsedValue { + pub fn into_concrete_value( + self, + mapping: &impl Fn(&str) -> Option, + ) -> anyhow::Result { + match self { + ParsedValue::Address(a) => Extra::move_value_into_concrete(MoveValue::Address( + a.into_account_address(mapping)?, + )), + ParsedValue::U8(u) => Extra::move_value_into_concrete(MoveValue::U8(u)), + ParsedValue::U16(u) => Extra::move_value_into_concrete(MoveValue::U16(u)), + ParsedValue::U32(u) => Extra::move_value_into_concrete(MoveValue::U32(u)), + ParsedValue::U64(u) => Extra::move_value_into_concrete(MoveValue::U64(u)), + ParsedValue::InferredNum(u) if u <= (u64::MAX.into()) => { + Extra::move_value_into_concrete(MoveValue::U64(u.try_into()?)) + } + ParsedValue::U128(u) => Extra::move_value_into_concrete(MoveValue::U128(u)), + ParsedValue::InferredNum(u) | ParsedValue::U256(u) => { + Extra::move_value_into_concrete(MoveValue::U256(u)) + } + ParsedValue::Bool(b) => Extra::move_value_into_concrete(MoveValue::Bool(b)), + ParsedValue::Vector(values) => Extra::concrete_vector( + values + .into_iter() + .map(|value| value.into_concrete_value(mapping)) + .collect::>()?, + ), + ParsedValue::Struct(values) => Extra::concrete_struct( + values + .into_iter() + .map(|value| value.into_concrete_value(mapping)) + .collect::>()?, + ), + ParsedValue::Custom(c) => Extra::into_concrete_value(c, mapping), + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs b/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs new file mode 100644 index 0000000000..6dcceaae2e --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs @@ -0,0 +1,322 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{convert::TryFrom, fmt, str::FromStr}; + +use hex::FromHex; +use rand::{rngs::OsRng, Rng}; +use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; + +/// A struct that represents an account address. +#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)] +pub struct AccountAddress([u8; AccountAddress::LENGTH]); + +impl AccountAddress { + pub const fn new(address: [u8; Self::LENGTH]) -> Self { + Self(address) + } + + /// The number of bytes in an address. + pub const LENGTH: usize = 32; + + /// Hex address: 0x0 + pub const ZERO: Self = Self([0u8; Self::LENGTH]); + + /// Hex address: 0x1 + pub const ONE: Self = Self::get_hex_address_one(); + + /// Hex address: 0x2 + pub const TWO: Self = Self::get_hex_address_two(); + + const fn get_hex_address_one() -> Self { + let mut addr = [0u8; AccountAddress::LENGTH]; + addr[AccountAddress::LENGTH - 1] = 1u8; + Self(addr) + } + + const fn get_hex_address_two() -> Self { + let mut addr = [0u8; AccountAddress::LENGTH]; + addr[AccountAddress::LENGTH - 1] = 2u8; + Self(addr) + } + + pub fn random() -> Self { + let mut rng = OsRng; + let buf: [u8; Self::LENGTH] = rng.gen(); + Self(buf) + } + + /// Return a canonical string representation of the address + /// Addresses are hex-encoded lowercase values of length ADDRESS_LENGTH (16, + /// 20, or 32 depending on the Move platform) + /// e.g., 0000000000000000000000000000000a, *not* + /// 0x0000000000000000000000000000000a, 0xa, or 0xA Note: this function + /// is guaranteed to be stable, and this is suitable for use inside Move + /// native functions or the VM. However, one can pass with_prefix=true + /// to get its representation with the 0x prefix. + pub fn to_canonical_string(&self, with_prefix: bool) -> String { + self.to_canonical_display(with_prefix).to_string() + } + + /// Implements Display for the address, with the prefix 0x if with_prefix is + /// true. + pub fn to_canonical_display(&self, with_prefix: bool) -> impl fmt::Display + '_ { + struct HexDisplay<'a> { + data: &'a [u8], + with_prefix: bool, + } + + impl<'a> fmt::Display for HexDisplay<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.with_prefix { + write!(f, "0x{}", hex::encode(self.data)) + } else { + write!(f, "{}", hex::encode(self.data)) + } + } + } + HexDisplay { + data: &self.0, + with_prefix, + } + } + + pub fn short_str_lossless(&self) -> String { + let hex_str = hex::encode(self.0).trim_start_matches('0').to_string(); + if hex_str.is_empty() { + "0".to_string() + } else { + hex_str + } + } + + pub fn to_vec(&self) -> Vec { + self.0.to_vec() + } + + pub fn into_bytes(self) -> [u8; Self::LENGTH] { + self.0 + } + + pub fn from_hex_literal(literal: &str) -> Result { + if !literal.starts_with("0x") { + return Err(AccountAddressParseError); + } + + let hex_len = literal.len() - 2; + + // If the string is too short, pad it + if hex_len < Self::LENGTH * 2 { + let mut hex_str = String::with_capacity(Self::LENGTH * 2); + for _ in 0..Self::LENGTH * 2 - hex_len { + hex_str.push('0'); + } + hex_str.push_str(&literal[2..]); + AccountAddress::from_hex(hex_str) + } else { + AccountAddress::from_hex(&literal[2..]) + } + } + + pub fn to_hex_literal(&self) -> String { + format!("0x{}", self.short_str_lossless()) + } + + pub fn from_hex>(hex: T) -> Result { + <[u8; Self::LENGTH]>::from_hex(hex) + .map_err(|_| AccountAddressParseError) + .map(Self) + } + + pub fn to_hex(&self) -> String { + format!("{:x}", self) + } + + pub fn from_bytes>(bytes: T) -> Result { + <[u8; Self::LENGTH]>::try_from(bytes.as_ref()) + .map_err(|_| AccountAddressParseError) + .map(Self) + } +} + +impl AsRef<[u8]> for AccountAddress { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl std::ops::Deref for AccountAddress { + type Target = [u8; Self::LENGTH]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Display for AccountAddress { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + write!(f, "{:x}", self) + } +} + +impl fmt::Debug for AccountAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:x}", self) + } +} + +impl fmt::LowerHex for AccountAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + + for byte in &self.0 { + write!(f, "{:02x}", byte)?; + } + + Ok(()) + } +} + +impl fmt::UpperHex for AccountAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + + for byte in &self.0 { + write!(f, "{:02X}", byte)?; + } + + Ok(()) + } +} + +impl From<[u8; AccountAddress::LENGTH]> for AccountAddress { + fn from(bytes: [u8; AccountAddress::LENGTH]) -> Self { + Self::new(bytes) + } +} + +impl TryFrom<&[u8]> for AccountAddress { + type Error = AccountAddressParseError; + + /// Tries to convert the provided byte array into Address. + fn try_from(bytes: &[u8]) -> Result { + Self::from_bytes(bytes) + } +} + +impl TryFrom> for AccountAddress { + type Error = AccountAddressParseError; + + /// Tries to convert the provided byte buffer into Address. + fn try_from(bytes: Vec) -> Result { + Self::from_bytes(bytes) + } +} + +impl From for Vec { + fn from(addr: AccountAddress) -> Vec { + addr.0.to_vec() + } +} + +impl From<&AccountAddress> for Vec { + fn from(addr: &AccountAddress) -> Vec { + addr.0.to_vec() + } +} + +impl From for [u8; AccountAddress::LENGTH] { + fn from(addr: AccountAddress) -> Self { + addr.0 + } +} + +impl From<&AccountAddress> for [u8; AccountAddress::LENGTH] { + fn from(addr: &AccountAddress) -> Self { + addr.0 + } +} + +impl From<&AccountAddress> for String { + fn from(addr: &AccountAddress) -> String { + ::hex::encode(addr.as_ref()) + } +} + +impl TryFrom for AccountAddress { + type Error = AccountAddressParseError; + + fn try_from(s: String) -> Result { + Self::from_hex(s) + } +} + +impl FromStr for AccountAddress { + type Err = AccountAddressParseError; + + fn from_str(s: &str) -> Result { + // Accept 0xADDRESS or ADDRESS + if let Ok(address) = AccountAddress::from_hex_literal(s) { + Ok(address) + } else { + Self::from_hex(s) + } + } +} + +impl<'de> Deserialize<'de> for AccountAddress { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = ::deserialize(deserializer)?; + AccountAddress::from_str(&s).map_err(D::Error::custom) + } else { + // In order to preserve the Serde data model and help analysis tools, + // make sure to wrap our value in a container with the same name + // as the original type. + #[derive(::serde::Deserialize)] + #[serde(rename = "AccountAddress")] + struct Value([u8; AccountAddress::LENGTH]); + + let value = Value::deserialize(deserializer)?; + Ok(AccountAddress::new(value.0)) + } + } +} + +impl Serialize for AccountAddress { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + if serializer.is_human_readable() { + self.to_hex().serialize(serializer) + } else { + // See comment in deserialize. + serializer.serialize_newtype_struct("AccountAddress", &self.0) + } + } +} + +#[derive(Clone, Copy, Debug)] +pub struct AccountAddressParseError; + +impl fmt::Display for AccountAddressParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + write!( + f, + "Unable to parse AccountAddress (must be hex string of length {})", + AccountAddress::LENGTH + ) + } +} + +impl std::error::Error for AccountAddressParseError {} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs b/identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs new file mode 100644 index 0000000000..5482efd902 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs @@ -0,0 +1,510 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + convert::From, + fmt::{self, Debug}, +}; + +use anyhow::Result as AResult; +use serde::{ + de::Error as DeError, + ser::{SerializeMap, SerializeSeq, SerializeStruct}, + Deserialize, Serialize, +}; + +use super::{ + account_address::AccountAddress, + annotated_visitor::{visit_struct, visit_value, Error as VError, Visitor}, + identifier::Identifier, + language_storage::{StructTag, TypeTag}, + runtime_value as R, u256, +}; + +/// In the `WithTypes` configuration, a Move struct gets serialized into a Serde +/// struct with this name +pub const MOVE_STRUCT_NAME: &str = "struct"; + +/// In the `WithTypes` configuration, a Move struct gets serialized into a Serde +/// struct with this as the first field +pub const MOVE_STRUCT_TYPE: &str = "type"; + +/// In the `WithTypes` configuration, a Move struct gets serialized into a Serde +/// struct with this as the second field +pub const MOVE_STRUCT_FIELDS: &str = "fields"; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct MoveStruct { + pub type_: StructTag, + pub fields: Vec<(Identifier, MoveValue)>, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum MoveValue { + U8(u8), + U64(u64), + U128(u128), + Bool(bool), + Address(AccountAddress), + Vector(Vec), + Struct(MoveStruct), + Signer(AccountAddress), + // NOTE: Added in bytecode version v6, do not reorder! + U16(u16), + U32(u32), + U256(u256::U256), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MoveFieldLayout { + pub name: Identifier, + pub layout: MoveTypeLayout, +} + +impl MoveFieldLayout { + pub fn new(name: Identifier, layout: MoveTypeLayout) -> Self { + Self { name, layout } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MoveStructLayout { + /// An decorated representation with both types and human-readable field + /// names + pub type_: StructTag, + pub fields: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum MoveTypeLayout { + #[serde(rename(serialize = "bool", deserialize = "bool"))] + Bool, + #[serde(rename(serialize = "u8", deserialize = "u8"))] + U8, + #[serde(rename(serialize = "u64", deserialize = "u64"))] + U64, + #[serde(rename(serialize = "u128", deserialize = "u128"))] + U128, + #[serde(rename(serialize = "address", deserialize = "address"))] + Address, + #[serde(rename(serialize = "vector", deserialize = "vector"))] + Vector(Box), + #[serde(rename(serialize = "struct", deserialize = "struct"))] + Struct(MoveStructLayout), + #[serde(rename(serialize = "signer", deserialize = "signer"))] + Signer, + + // NOTE: Added in bytecode version v6, do not reorder! + #[serde(rename(serialize = "u16", deserialize = "u16"))] + U16, + #[serde(rename(serialize = "u32", deserialize = "u32"))] + U32, + #[serde(rename(serialize = "u256", deserialize = "u256"))] + U256, +} + +impl MoveValue { + /// TODO (annotated-visitor): Port legacy uses of this method to + /// `BoundedVisitor`. + pub fn simple_deserialize(blob: &[u8], ty: &MoveTypeLayout) -> AResult { + Ok(bcs::from_bytes_seed(ty, blob)?) + } + + /// Deserialize `blob` as a Move value with the given `ty`-pe layout, and + /// visit its sub-structure with the given `visitor`. The visitor + /// dictates the return value that is built up during deserialization. + /// + /// # Nested deserialization + /// + /// Vectors and structs are nested structures that can be met during + /// deserialization. Visitors are passed a driver (`VecDriver` or + /// `StructDriver` correspondingly) which controls how nested elements + /// or fields are visited including whether a given nested element/field is + /// explored, which visitor to use (the visitor can pass `self` to + /// recursively explore them) and whether a given element is visited or + /// skipped. + /// + /// The visitor may leave elements unvisited at the end of the vector or + /// struct, which implicitly skips them. + /// + /// # Errors + /// + /// Deserialization can fail because of an issue in the serialized format + /// (data doesn't match layout, unexpected bytes or trailing bytes), or + /// a custom error expressed by the visitor. + pub fn visit_deserialize( + mut blob: &[u8], + ty: &MoveTypeLayout, + visitor: &mut V, + ) -> AResult + where + V::Error: std::error::Error + Send + Sync + 'static, + { + let res = visit_value(&mut blob, ty, visitor)?; + if blob.is_empty() { + Ok(res) + } else { + Err(VError::TrailingBytes(blob.len()).into()) + } + } + + pub fn simple_serialize(&self) -> Option> { + bcs::to_bytes(self).ok() + } + + pub fn undecorate(self) -> R::MoveValue { + match self { + Self::Struct(s) => R::MoveValue::Struct(s.undecorate()), + Self::Vector(vals) => { + R::MoveValue::Vector(vals.into_iter().map(MoveValue::undecorate).collect()) + } + MoveValue::U8(u) => R::MoveValue::U8(u), + MoveValue::U64(u) => R::MoveValue::U64(u), + MoveValue::U128(u) => R::MoveValue::U128(u), + MoveValue::Bool(b) => R::MoveValue::Bool(b), + MoveValue::Address(a) => R::MoveValue::Address(a), + MoveValue::Signer(s) => R::MoveValue::Signer(s), + MoveValue::U16(u) => R::MoveValue::U16(u), + MoveValue::U32(u) => R::MoveValue::U32(u), + MoveValue::U256(u) => R::MoveValue::U256(u), + } + } +} + +pub fn serialize_values<'a, I>(vals: I) -> Vec> +where + I: IntoIterator, +{ + vals.into_iter() + .map(|val| { + val.simple_serialize() + .expect("serialization should succeed") + }) + .collect() +} + +impl MoveStruct { + pub fn new(type_: StructTag, fields: Vec<(Identifier, MoveValue)>) -> Self { + Self { type_, fields } + } + + /// TODO (annotated-visitor): Port legacy uses of this method to + /// `BoundedVisitor`. + pub fn simple_deserialize(blob: &[u8], ty: &MoveStructLayout) -> AResult { + Ok(bcs::from_bytes_seed(ty, blob)?) + } + + /// Like `MoveValue::visit_deserialize` (see for details), but specialized + /// to visiting a struct (the `blob` is known to be a serialized Move + /// struct, and the layout is a `MoveStructLayout`). + pub fn visit_deserialize( + mut blob: &[u8], + ty: &MoveStructLayout, + visitor: &mut V, + ) -> AResult + where + V::Error: std::error::Error + Send + Sync + 'static, + { + let res = visit_struct(&mut blob, ty, visitor)?; + if blob.is_empty() { + Ok(res) + } else { + Err(VError::TrailingBytes(blob.len()).into()) + } + } + + pub fn into_fields(self) -> Vec { + self.fields.into_iter().map(|(_, f)| f).collect() + } + + pub fn undecorate(self) -> R::MoveStruct { + R::MoveStruct( + self.into_fields() + .into_iter() + .map(MoveValue::undecorate) + .collect(), + ) + } +} + +impl MoveStructLayout { + pub fn new(type_: StructTag, fields: Vec) -> Self { + Self { type_, fields } + } + + pub fn into_fields(self) -> Vec { + self.fields.into_iter().map(|f| f.layout).collect() + } +} + +impl<'d> serde::de::DeserializeSeed<'d> for &MoveTypeLayout { + type Value = MoveValue; + fn deserialize>( + self, + deserializer: D, + ) -> Result { + match self { + MoveTypeLayout::Bool => bool::deserialize(deserializer).map(MoveValue::Bool), + MoveTypeLayout::U8 => u8::deserialize(deserializer).map(MoveValue::U8), + MoveTypeLayout::U16 => u16::deserialize(deserializer).map(MoveValue::U16), + MoveTypeLayout::U32 => u32::deserialize(deserializer).map(MoveValue::U32), + MoveTypeLayout::U64 => u64::deserialize(deserializer).map(MoveValue::U64), + MoveTypeLayout::U128 => u128::deserialize(deserializer).map(MoveValue::U128), + MoveTypeLayout::U256 => u256::U256::deserialize(deserializer).map(MoveValue::U256), + MoveTypeLayout::Address => { + AccountAddress::deserialize(deserializer).map(MoveValue::Address) + } + MoveTypeLayout::Signer => { + AccountAddress::deserialize(deserializer).map(MoveValue::Signer) + } + MoveTypeLayout::Struct(ty) => Ok(MoveValue::Struct(ty.deserialize(deserializer)?)), + MoveTypeLayout::Vector(layout) => Ok(MoveValue::Vector( + deserializer.deserialize_seq(VectorElementVisitor(layout))?, + )), + } + } +} + +struct VectorElementVisitor<'a>(&'a MoveTypeLayout); + +impl<'d, 'a> serde::de::Visitor<'d> for VectorElementVisitor<'a> { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("Vector") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'d>, + { + let mut vals = Vec::new(); + while let Some(elem) = seq.next_element_seed(self.0)? { + vals.push(elem) + } + Ok(vals) + } +} + +struct DecoratedStructFieldVisitor<'a>(&'a [MoveFieldLayout]); + +impl<'d, 'a> serde::de::Visitor<'d> for DecoratedStructFieldVisitor<'a> { + type Value = Vec<(Identifier, MoveValue)>; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("Struct") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'d>, + { + let mut vals = Vec::new(); + for (i, layout) in self.0.iter().enumerate() { + match seq.next_element_seed(layout)? { + Some(elem) => vals.push(elem), + None => return Err(A::Error::invalid_length(i, &self)), + } + } + Ok(vals) + } +} + +impl<'d> serde::de::DeserializeSeed<'d> for &MoveFieldLayout { + type Value = (Identifier, MoveValue); + + fn deserialize>( + self, + deserializer: D, + ) -> Result { + Ok((self.name.clone(), self.layout.deserialize(deserializer)?)) + } +} + +impl<'d> serde::de::DeserializeSeed<'d> for &MoveStructLayout { + type Value = MoveStruct; + + fn deserialize>( + self, + deserializer: D, + ) -> Result { + let fields = deserializer + .deserialize_tuple(self.fields.len(), DecoratedStructFieldVisitor(&self.fields))?; + Ok(MoveStruct { + type_: self.type_.clone(), + fields, + }) + } +} + +impl serde::Serialize for MoveValue { + fn serialize(&self, serializer: S) -> Result { + match self { + MoveValue::Struct(s) => s.serialize(serializer), + MoveValue::Bool(b) => serializer.serialize_bool(*b), + MoveValue::U8(i) => serializer.serialize_u8(*i), + MoveValue::U16(i) => serializer.serialize_u16(*i), + MoveValue::U32(i) => serializer.serialize_u32(*i), + MoveValue::U64(i) => serializer.serialize_u64(*i), + MoveValue::U128(i) => serializer.serialize_u128(*i), + MoveValue::U256(i) => i.serialize(serializer), + MoveValue::Address(a) => a.serialize(serializer), + MoveValue::Signer(a) => a.serialize(serializer), + MoveValue::Vector(v) => { + let mut t = serializer.serialize_seq(Some(v.len()))?; + for val in v { + t.serialize_element(val)?; + } + t.end() + } + } + } +} + +struct MoveFields<'a>(&'a [(Identifier, MoveValue)]); + +impl<'a> serde::Serialize for MoveFields<'a> { + fn serialize(&self, serializer: S) -> Result { + let mut t = serializer.serialize_map(Some(self.0.len()))?; + for (f, v) in self.0.iter() { + t.serialize_entry(f, v)?; + } + t.end() + } +} + +impl serde::Serialize for MoveStruct { + fn serialize(&self, serializer: S) -> Result { + // Serialize a Move struct as Serde struct type named `struct `with two fields + // named `type` and `fields`. `fields` will get serialized as a Serde + // map. Unfortunately, we can't serialize this in the logical way: as a + // Serde struct named `type` with a field for each of `fields` because + // serde insists that struct and field names be `'static &str`'s + let mut t = serializer.serialize_struct(MOVE_STRUCT_NAME, 2)?; + // serialize type as string (e.g., + // 0x0::ModuleName::StructName) instead of (e.g. + // { address: 0x0...0, module: ModuleName, name: StructName, type_args: + // [TypeArg1, TypeArg2]}) + t.serialize_field(MOVE_STRUCT_TYPE, &self.type_.to_string())?; + t.serialize_field(MOVE_STRUCT_FIELDS, &MoveFields(&self.fields))?; + t.end() + } +} + +impl fmt::Display for MoveTypeLayout { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + use MoveTypeLayout::*; + match self { + Bool => write!(f, "bool"), + U8 => write!(f, "u8"), + U16 => write!(f, "u16"), + U32 => write!(f, "u32"), + U64 => write!(f, "u64"), + U128 => write!(f, "u128"), + U256 => write!(f, "u256"), + Address => write!(f, "address"), + Signer => write!(f, "signer"), + Vector(typ) if f.alternate() => write!(f, "vector<{typ:#}>"), + Vector(typ) => write!(f, "vector<{typ}>"), + Struct(s) if f.alternate() => write!(f, "{s:#}"), + Struct(s) => write!(f, "{s}"), + } + } +} + +/// Helper type that uses `T`'s `Display` implementation as its own `Debug` +/// implementation, to allow other `Display` implementations in this module to +/// take advantage of the structured formatting helpers that Rust uses for its +/// own debug types. +struct DebugAsDisplay<'a, T>(&'a T); +impl<'a, T: fmt::Display> fmt::Debug for DebugAsDisplay<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "{:#}", self.0) + } else { + write!(f, "{}", self.0) + } + } +} + +impl fmt::Display for MoveStructLayout { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + use DebugAsDisplay as DD; + write!(f, "struct ")?; + write!(f, "{} ", self.type_)?; + let mut map = f.debug_map(); + for field in &self.fields { + map.entry(&DD(&field.name), &DD(&field.layout)); + } + map.finish() + } +} + +impl From<&MoveTypeLayout> for TypeTag { + fn from(val: &MoveTypeLayout) -> TypeTag { + match val { + MoveTypeLayout::Address => TypeTag::Address, + MoveTypeLayout::Bool => TypeTag::Bool, + MoveTypeLayout::U8 => TypeTag::U8, + MoveTypeLayout::U16 => TypeTag::U16, + MoveTypeLayout::U32 => TypeTag::U32, + MoveTypeLayout::U64 => TypeTag::U64, + MoveTypeLayout::U128 => TypeTag::U128, + MoveTypeLayout::U256 => TypeTag::U256, + MoveTypeLayout::Signer => TypeTag::Signer, + MoveTypeLayout::Vector(v) => { + let inner_type = &**v; + TypeTag::Vector(Box::new(inner_type.into())) + } + MoveTypeLayout::Struct(v) => TypeTag::Struct(Box::new(v.into())), + } + } +} + +impl From<&MoveStructLayout> for StructTag { + fn from(val: &MoveStructLayout) -> StructTag { + val.type_.clone() + } +} + +impl fmt::Display for MoveValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MoveValue::U8(u) => write!(f, "{}u8", u), + MoveValue::U16(u) => write!(f, "{}u16", u), + MoveValue::U32(u) => write!(f, "{}u32", u), + MoveValue::U64(u) => write!(f, "{}u64", u), + MoveValue::U128(u) => write!(f, "{}u128", u), + MoveValue::U256(u) => write!(f, "{}u256", u), + MoveValue::Bool(false) => write!(f, "false"), + MoveValue::Bool(true) => write!(f, "true"), + MoveValue::Address(a) => write!(f, "{}", a.to_hex_literal()), + MoveValue::Signer(a) => write!(f, "signer({})", a.to_hex_literal()), + MoveValue::Vector(v) => { + use DebugAsDisplay as DD; + write!(f, "vector")?; + let mut list = f.debug_list(); + for val in v { + list.entry(&DD(val)); + } + list.finish() + } + MoveValue::Struct(s) => fmt::Display::fmt(s, f), + } + } +} + +impl fmt::Display for MoveStruct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use DebugAsDisplay as DD; + fmt::Display::fmt(&self.type_, f)?; + write!(f, " ")?; + let mut map = f.debug_map(); + for (field, value) in &self.fields { + map.entry(&DD(field), &DD(value)); + } + map.finish() + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs b/identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs new file mode 100644 index 0000000000..f30f151e49 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs @@ -0,0 +1,379 @@ +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::io::Read; + +use super::{ + account_address::AccountAddress, + annotated_value::{MoveFieldLayout, MoveStructLayout, MoveTypeLayout}, + u256::U256, +}; + +/// Visitors can be used for building values out of a serialized Move struct or +/// value. +pub trait Visitor { + type Value; + + /// Visitors can return any error as long as it can represent an error from + /// the visitor itself. The easiest way to achieve this is to use + /// `thiserror`: + /// + /// ```rust,no_doc + /// #[derive(thiserror::Error)] + /// enum Error { + /// #[error(transparent)] + /// Visitor(#[from] annotated_visitor::Error) + /// + /// // Custom error variants ... + /// } + /// ``` + type Error: From; + + fn visit_u8(&mut self, value: u8) -> Result; + fn visit_u16(&mut self, value: u16) -> Result; + fn visit_u32(&mut self, value: u32) -> Result; + fn visit_u64(&mut self, value: u64) -> Result; + fn visit_u128(&mut self, value: u128) -> Result; + fn visit_u256(&mut self, value: U256) -> Result; + fn visit_bool(&mut self, value: bool) -> Result; + fn visit_address(&mut self, value: AccountAddress) -> Result; + fn visit_signer(&mut self, value: AccountAddress) -> Result; + + fn visit_vector( + &mut self, + driver: &mut VecDriver<'_, '_, '_>, + ) -> Result; + + fn visit_struct( + &mut self, + driver: &mut StructDriver<'_, '_, '_>, + ) -> Result; +} + +/// A traversal is a special kind of visitor that doesn't return any values. The +/// trait comes with default implementations for every variant that do nothing, +/// allowing an implementor to focus on only the cases they care about. +/// +/// Note that the default implementation for structs and vectors recurse down +/// into their elements. A traversal that doesn't want to look inside structs +/// and vectors needs to provide a custom implementation with an empty body: +/// +/// ```rust,no_run +/// fn traverse_vector(&mut self, _: &mut VecDriver) -> Result<(), Self::Error> { +/// Ok(()) +/// } +/// ``` +pub trait Traversal { + type Error: From; + + fn traverse_u8(&mut self, _value: u8) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u16(&mut self, _value: u16) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u32(&mut self, _value: u32) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u64(&mut self, _value: u64) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u128(&mut self, _value: u128) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u256(&mut self, _value: U256) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_bool(&mut self, _value: bool) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_address(&mut self, _value: AccountAddress) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_signer(&mut self, _value: AccountAddress) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_vector(&mut self, driver: &mut VecDriver<'_, '_, '_>) -> Result<(), Self::Error> { + while driver.next_element(self)?.is_some() {} + Ok(()) + } + + fn traverse_struct( + &mut self, + driver: &mut StructDriver<'_, '_, '_>, + ) -> Result<(), Self::Error> { + while driver.next_field(self)?.is_some() {} + Ok(()) + } +} + +/// Default implementation converting any traversal into a visitor. +impl Visitor for T { + type Value = (); + type Error = T::Error; + + fn visit_u8(&mut self, value: u8) -> Result { + self.traverse_u8(value) + } + + fn visit_u16(&mut self, value: u16) -> Result { + self.traverse_u16(value) + } + + fn visit_u32(&mut self, value: u32) -> Result { + self.traverse_u32(value) + } + + fn visit_u64(&mut self, value: u64) -> Result { + self.traverse_u64(value) + } + + fn visit_u128(&mut self, value: u128) -> Result { + self.traverse_u128(value) + } + + fn visit_u256(&mut self, value: U256) -> Result { + self.traverse_u256(value) + } + + fn visit_bool(&mut self, value: bool) -> Result { + self.traverse_bool(value) + } + + fn visit_address(&mut self, value: AccountAddress) -> Result { + self.traverse_address(value) + } + + fn visit_signer(&mut self, value: AccountAddress) -> Result { + self.traverse_signer(value) + } + + fn visit_vector( + &mut self, + driver: &mut VecDriver<'_, '_, '_>, + ) -> Result { + self.traverse_vector(driver) + } + + fn visit_struct( + &mut self, + driver: &mut StructDriver<'_, '_, '_>, + ) -> Result { + self.traverse_struct(driver) + } +} + +/// Exposes information about a vector being visited (the element layout) to a +/// visitor implementation, and allows that visitor to progress the traversal +/// (by visiting or skipping elements). +pub struct VecDriver<'r, 'b, 'l> { + bytes: &'r mut &'b [u8], + layout: &'l MoveTypeLayout, + len: u64, + off: u64, +} + +/// Exposes information about a struct being visited (its layout, details about +/// the next field to be visited) to a visitor implementation, and allows that +/// visitor to progress the traversal (by visiting or skipping fields). +pub struct StructDriver<'r, 'b, 'l> { + bytes: &'r mut &'b [u8], + layout: &'l MoveStructLayout, + off: usize, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("unexpected end of input")] + UnexpectedEof, + + #[error("unexpected byte: {0}")] + UnexpectedByte(u8), + + #[error("trailing {0} byte(s) at the end of input")] + TrailingBytes(usize), +} + +/// The null traversal implements `Traversal` and `Visitor` but without doing +/// anything (does not return a value, and does not modify any state). This is +/// useful for skipping over parts of the value structure. +pub struct NullTraversal; + +impl Traversal for NullTraversal { + type Error = Error; +} + +#[allow(clippy::len_without_is_empty)] +impl<'r, 'b, 'l> VecDriver<'r, 'b, 'l> { + fn new(bytes: &'r mut &'b [u8], layout: &'l MoveTypeLayout, len: u64) -> Self { + Self { + bytes, + layout, + len, + off: 0, + } + } + + /// Type layout for the vector's inner type. + pub fn element_layout(&self) -> &'l MoveTypeLayout { + self.layout + } + + /// The number of elements in this vector + pub fn len(&self) -> u64 { + self.len + } + + /// Returns whether or not there are more elements to visit in this vector. + pub fn has_element(&self) -> bool { + self.off < self.len + } + + /// Visit the next element in the vector. The driver accepts a visitor to + /// use for this element, allowing the visitor to be changed on + /// recursive calls or even between elements in the same vector. + /// + /// Returns `Ok(None)` if there are no more elements in the vector, `Ok(v)` + /// if there was an element and it was successfully visited (where `v` + /// is the value returned by the visitor) or an error if there was an + /// underlying deserialization error, or an error during visitation. + pub fn next_element( + &mut self, + visitor: &mut V, + ) -> Result, V::Error> { + Ok(if self.off >= self.len { + None + } else { + let res = visit_value(self.bytes, self.layout, visitor)?; + self.off += 1; + Some(res) + }) + } + + /// Skip the next element in this vector. Returns whether there was an + /// element to skip or not on success, or an error if there was an + /// underlying deserialization error. + pub fn skip_element(&mut self) -> Result { + self.next_element(&mut NullTraversal).map(|v| v.is_some()) + } +} + +impl<'r, 'b, 'l> StructDriver<'r, 'b, 'l> { + fn new(bytes: &'r mut &'b [u8], layout: &'l MoveStructLayout) -> Self { + Self { + bytes, + layout, + off: 0, + } + } + + /// The layout of the struct being visited. + pub fn struct_layout(&self) -> &'l MoveStructLayout { + self.layout + } + + /// The layout of the next field to be visited (if there is one), or `None` + /// otherwise. + pub fn peek_field(&self) -> Option<&'l MoveFieldLayout> { + self.layout.fields.get(self.off) + } + + /// Visit the next field in the struct. The driver accepts a visitor to use + /// for this field, allowing the visitor to be changed on recursive + /// calls or even between fields in the same struct. + /// + /// Returns `Ok(None)` if there are no more fields in the struct, `Ok((f, + /// v))` if there was an field and it was successfully visited (where + /// `v` is the value returned by the visitor, and `f` is the layout of + /// the field that was visited) or an error if there was an underlying + /// deserialization error, or an error during visitation. + pub fn next_field( + &mut self, + visitor: &mut V, + ) -> Result, V::Error> { + let Some(field) = self.peek_field() else { + return Ok(None); + }; + + let res = visit_value(self.bytes, &field.layout, visitor)?; + self.off += 1; + Ok(Some((field, res))) + } + + /// Skip the next field. Returns the layout of the field that was visited if + /// there was one, or `None` if there was none. Can return an error if + /// there was a deserialization error. + pub fn skip_field(&mut self) -> Result, Error> { + self.next_field(&mut NullTraversal) + .map(|res| res.map(|(f, _)| f)) + } +} + +/// Visit a serialized Move value with the provided `layout`, held in `bytes`, +/// using the provided visitor to build a value out of it. See +/// `annotated_value::MoveValue::visit_deserialize` for details. +pub(crate) fn visit_value( + bytes: &mut &[u8], + layout: &MoveTypeLayout, + visitor: &mut V, +) -> Result { + use MoveTypeLayout as L; + + match layout { + L::Bool => match read_exact::<1>(bytes)? { + [0] => visitor.visit_bool(false), + [1] => visitor.visit_bool(true), + [b] => Err(Error::UnexpectedByte(b).into()), + }, + + L::U8 => visitor.visit_u8(u8::from_le_bytes(read_exact::<1>(bytes)?)), + L::U16 => visitor.visit_u16(u16::from_le_bytes(read_exact::<2>(bytes)?)), + L::U32 => visitor.visit_u32(u32::from_le_bytes(read_exact::<4>(bytes)?)), + L::U64 => visitor.visit_u64(u64::from_le_bytes(read_exact::<8>(bytes)?)), + L::U128 => visitor.visit_u128(u128::from_le_bytes(read_exact::<16>(bytes)?)), + L::U256 => visitor.visit_u256(U256::from_le_bytes(&read_exact::<32>(bytes)?)), + L::Address => visitor.visit_address(AccountAddress::new(read_exact::<32>(bytes)?)), + L::Signer => visitor.visit_signer(AccountAddress::new(read_exact::<32>(bytes)?)), + + L::Vector(l) => { + let len = leb128::read::unsigned(bytes).map_err(|_| Error::UnexpectedEof)?; + let mut driver = VecDriver::new(bytes, l.as_ref(), len); + let res = visitor.visit_vector(&mut driver)?; + while driver.skip_element()? {} + Ok(res) + } + + L::Struct(l) => visit_struct(bytes, l, visitor), + } +} + +/// Like `visit_value` but specialized to visiting a struct (where the `bytes` +/// is known to be a serialized move struct), and the layout is a struct layout. +pub(crate) fn visit_struct( + bytes: &mut &[u8], + layout: &MoveStructLayout, + visitor: &mut V, +) -> Result { + let mut driver = StructDriver::new(bytes, layout); + let res = visitor.visit_struct(&mut driver)?; + while driver.skip_field()?.is_some() {} + Ok(res) +} + +fn read_exact(bytes: &mut &[u8]) -> Result<[u8; N], Error> { + let mut buf = [0u8; N]; + bytes + .read_exact(&mut buf) + .map_err(|_| Error::UnexpectedEof)?; + Ok(buf) +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs b/identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs new file mode 100644 index 0000000000..a9a8dbd4ff --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs @@ -0,0 +1,217 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::str::FromStr; +use core::ops::Deref; +use core::fmt; +use std::borrow::Borrow; + +use ref_cast::RefCast; + +use serde::Deserialize; +use serde::Serialize; + +use anyhow::{bail, Result}; + + +/// Return true if this character can appear in a Move identifier. +/// +/// Note: there are stricter restrictions on whether a character can begin a +/// Move identifier--only alphabetic characters are allowed here. +#[inline] +pub const fn is_valid_identifier_char(c: char) -> bool { + matches!(c, '_' | 'a'..='z' | 'A'..='Z' | '0'..='9') +} + +/// Returns `true` if all bytes in `b` after the offset `start_offset` are valid +/// ASCII identifier characters. +const fn all_bytes_valid(b: &[u8], start_offset: usize) -> bool { + let mut i = start_offset; + // TODO(philiphayes): use for loop instead of while loop when it's stable in + // const fn's. + while i < b.len() { + if !is_valid_identifier_char(b[i] as char) { + return false; + } + i += 1; + } + true +} + +/// Describes what identifiers are allowed. +/// +/// For now this is deliberately restrictive -- we would like to evolve this in +/// the future. +// TODO: "" is coded as an exception. It should be removed once +// CompiledScript goes away. Note: needs to be pub as it's used in the +// `ident_str!` macro. +pub const fn is_valid(s: &str) -> bool { + // Rust const fn's don't currently support slicing or indexing &str's, so we + // have to operate on the underlying byte slice. This is not a problem as + // valid identifiers are (currently) ASCII-only. + let b = s.as_bytes(); + match b { + b"" => true, + [b'a'..=b'z', ..] | [b'A'..=b'Z', ..] => all_bytes_valid(b, 1), + [b'_', ..] if b.len() > 1 => all_bytes_valid(b, 1), + _ => false, + } +} + +/// An owned identifier. +/// +/// For more details, see the module level documentation. +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)] +pub struct Identifier(Box); +// An identifier cannot be mutated so use Box instead of String -- it is 1 +// word smaller. + +impl Identifier { + /// Creates a new `Identifier` instance. + pub fn new(s: impl Into>) -> Result { + let s = s.into(); + if Self::is_valid(&s) { + Ok(Self(s)) + } else { + bail!("Invalid identifier '{}'", s); + } + } + + /// Returns true if this string is a valid identifier. + pub fn is_valid(s: impl AsRef) -> bool { + is_valid(s.as_ref()) + } + + /// Returns if this identifier is ``. + /// TODO: remove once we fully separate CompiledScript & CompiledModule. + pub fn is_self(&self) -> bool { + &*self.0 == "" + } + + /// Converts a vector of bytes to an `Identifier`. + pub fn from_utf8(vec: Vec) -> Result { + let s = String::from_utf8(vec)?; + Self::new(s) + } + + /// Creates a borrowed version of `self`. + pub fn as_ident_str(&self) -> &IdentStr { + self + } + + /// Converts this `Identifier` into a `String`. + /// + /// This is not implemented as a `From` trait to discourage automatic + /// conversions -- these conversions should not typically happen. + pub fn into_string(self) -> String { + self.0.into() + } + + /// Converts this `Identifier` into a UTF-8-encoded byte sequence. + pub fn into_bytes(self) -> Vec { + self.into_string().into_bytes() + } +} + +impl FromStr for Identifier { + type Err = anyhow::Error; + + fn from_str(data: &str) -> Result { + Self::new(data) + } +} + +impl From<&IdentStr> for Identifier { + fn from(ident_str: &IdentStr) -> Self { + ident_str.to_owned() + } +} + +impl AsRef for Identifier { + fn as_ref(&self) -> &IdentStr { + self + } +} + +impl Deref for Identifier { + type Target = IdentStr; + + fn deref(&self) -> &IdentStr { + // Identifier and IdentStr maintain the same invariants, so it is safe to + // convert. + IdentStr::ref_cast(&self.0) + } +} + +impl fmt::Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", &self.0) + } +} + +/// A borrowed identifier. +/// +/// For more details, see the module level documentation. +#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCast)] +#[repr(transparent)] +pub struct IdentStr(str); + +impl IdentStr { + pub fn new(s: &str) -> Result<&IdentStr> { + if Self::is_valid(s) { + Ok(IdentStr::ref_cast(s)) + } else { + bail!("Invalid identifier '{}'", s); + } + } + + /// Returns true if this string is a valid identifier. + pub fn is_valid(s: impl AsRef) -> bool { + is_valid(s.as_ref()) + } + + /// Returns the length of `self` in bytes. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if `self` has a length of zero bytes. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Converts `self` to a `&str`. + /// + /// This is not implemented as a `From` trait to discourage automatic + /// conversions -- these conversions should not typically happen. + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Converts `self` to a byte slice. + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl Borrow for Identifier { + fn borrow(&self) -> &IdentStr { + self + } +} + +impl ToOwned for IdentStr { + type Owned = Identifier; + + fn to_owned(&self) -> Identifier { + Identifier(self.0.into()) + } +} + +impl fmt::Display for IdentStr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", &self.0) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs b/identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs new file mode 100644 index 0000000000..21dc5e3c68 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs @@ -0,0 +1,411 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; + +use serde::{Deserialize, Serialize}; + +use super::{ + account_address::AccountAddress, +}; + +use super::identifier::{IdentStr, Identifier}; +use super::parser::{parse_type_tag, parse_struct_tag}; + +pub const CODE_TAG: u8 = 0; +pub const RESOURCE_TAG: u8 = 1; + +/// Hex address: 0x1 +pub const CORE_CODE_ADDRESS: AccountAddress = AccountAddress::ONE; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)] +pub enum TypeTag { + // alias for compatibility with old json serialized data. + #[serde(rename = "bool", alias = "Bool")] + Bool, + #[serde(rename = "u8", alias = "U8")] + U8, + #[serde(rename = "u64", alias = "U64")] + U64, + #[serde(rename = "u128", alias = "U128")] + U128, + #[serde(rename = "address", alias = "Address")] + Address, + #[serde(rename = "signer", alias = "Signer")] + Signer, + #[serde(rename = "vector", alias = "Vector")] + Vector(Box), + #[serde(rename = "struct", alias = "Struct")] + Struct(Box), + + // NOTE: Added in bytecode version v6, do not reorder! + #[serde(rename = "u16", alias = "U16")] + U16, + #[serde(rename = "u32", alias = "U32")] + U32, + #[serde(rename = "u256", alias = "U256")] + U256, +} + +impl TypeTag { + /// Return a canonical string representation of the type. All types are + /// represented using their source syntax: + /// "u8", "u64", "u128", "bool", "address", "vector", "signer" for ground + /// types. Struct types are represented as fully qualified type names; + /// e.g. `00000000000000000000000000000001::string::String` or + /// `0000000000000000000000000000000a::module_name1::type_name1<0000000000000000000000000000000a::module_name2::type_name2>` + /// With or without the prefix 0x depending on the `with_prefix` flag. + /// Addresses are hex-encoded lowercase values of length ADDRESS_LENGTH (16, + /// 20, or 32 depending on the Move platform) Note: this function is + /// guaranteed to be stable, and this is suitable for use inside + /// Move native functions or the VM. By contrast, the `Display` + /// implementation is subject to change and should not be used inside + /// stable code. + pub fn to_canonical_string(&self, with_prefix: bool) -> String { + self.to_canonical_display(with_prefix).to_string() + } + + /// Return the canonical string representation of the TypeTag conditionally + /// with prefix 0x + pub fn to_canonical_display(&self, with_prefix: bool) -> impl std::fmt::Display + '_ { + struct CanonicalDisplay<'a> { + data: &'a TypeTag, + with_prefix: bool, + } + + impl std::fmt::Display for CanonicalDisplay<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self.data { + TypeTag::Bool => write!(f, "bool"), + TypeTag::U8 => write!(f, "u8"), + TypeTag::U16 => write!(f, "u16"), + TypeTag::U32 => write!(f, "u32"), + TypeTag::U64 => write!(f, "u64"), + TypeTag::U128 => write!(f, "u128"), + TypeTag::U256 => write!(f, "u256"), + TypeTag::Address => write!(f, "address"), + TypeTag::Signer => write!(f, "signer"), + TypeTag::Vector(t) => { + write!(f, "vector<{}>", t.to_canonical_display(self.with_prefix)) + } + TypeTag::Struct(s) => write!(f, "{}", s.to_canonical_display(self.with_prefix)), + } + } + } + + CanonicalDisplay { + data: self, + with_prefix, + } + } +} + +impl FromStr for TypeTag { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + parse_type_tag(s) + } +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)] +pub struct StructTag { + pub address: AccountAddress, + pub module: Identifier, + pub name: Identifier, + // alias for compatibility with old json serialized data. + #[serde(rename = "type_args", alias = "type_params")] + pub type_params: Vec, +} + +impl StructTag { + pub fn access_vector(&self) -> Vec { + let mut key = vec![RESOURCE_TAG]; + key.append(&mut bcs::to_bytes(self).unwrap()); + key + } + + /// Returns true if this is a `StructTag` for an `std::ascii::String` struct + /// defined in the standard library at address `move_std_addr`. + pub fn is_ascii_string(&self, move_std_addr: &AccountAddress) -> bool { + self.address == *move_std_addr + && self.module.as_str().eq("ascii") + && self.name.as_str().eq("String") + } + + /// Returns true if this is a `StructTag` for an `std::string::String` + /// struct defined in the standard library at address `move_std_addr`. + pub fn is_std_string(&self, move_std_addr: &AccountAddress) -> bool { + self.address == *move_std_addr + && self.module.as_str().eq("string") + && self.name.as_str().eq("String") + } + + pub fn module_id(&self) -> ModuleId { + ModuleId::new(self.address, self.module.to_owned()) + } + + /// Return a canonical string representation of the struct. + /// Struct types are represented as fully qualified type names; e.g. + /// `00000000000000000000000000000001::string::String`, + /// `0000000000000000000000000000000a::module_name1::type_name1<0000000000000000000000000000000a::module_name2::type_name2>`, + /// or `0000000000000000000000000000000a::module_name2::type_name2. With or without the prefix 0x depending on the `with_prefix` + /// flag. Addresses are hex-encoded lowercase values of length + /// ADDRESS_LENGTH (16, 20, or 32 depending on the Move platform) + /// Note: this function is guaranteed to be stable, and this is suitable for + /// use inside Move native functions or the VM. By contrast, the + /// `Display` implementation is subject to change and should not be used + /// inside stable code. + pub fn to_canonical_string(&self, with_prefix: bool) -> String { + self.to_canonical_display(with_prefix).to_string() + } + + /// Implements the canonical string representation of the StructTag with the + /// prefix 0x + pub fn to_canonical_display(&self, with_prefix: bool) -> impl std::fmt::Display + '_ { + struct CanonicalDisplay<'a> { + data: &'a StructTag, + with_prefix: bool, + } + + impl std::fmt::Display for CanonicalDisplay<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}::{}::{}", + self.data.address.to_canonical_display(self.with_prefix), + self.data.module, + self.data.name + )?; + + if let Some(first_ty) = self.data.type_params.first() { + write!(f, "<")?; + write!(f, "{}", first_ty.to_canonical_display(self.with_prefix))?; + for ty in self.data.type_params.iter().skip(1) { + // Note that unlike Display for StructTag, there is no space between the + // comma and canonical display. This follows the + // original to_canonical_string() implementation. + write!(f, ",{}", ty.to_canonical_display(self.with_prefix))?; + } + write!(f, ">")?; + } + Ok(()) + } + } + + CanonicalDisplay { + data: self, + with_prefix, + } + } +} + +impl FromStr for StructTag { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + parse_struct_tag(s) + } +} + +/// Represents the initial key into global storage where we first index by the +/// address, and then the struct tag +#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)] +pub struct ResourceKey { + pub address: AccountAddress, + pub type_: StructTag, +} + +impl ResourceKey { + pub fn address(&self) -> AccountAddress { + self.address + } + + pub fn type_(&self) -> &StructTag { + &self.type_ + } +} + +impl ResourceKey { + pub fn new(address: AccountAddress, type_: StructTag) -> Self { + ResourceKey { address, type_ } + } +} + +/// Represents the initial key into global storage where we first index by the +/// address, and then the struct tag +#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)] +pub struct ModuleId { + address: AccountAddress, + name: Identifier, +} + +impl From for (AccountAddress, Identifier) { + fn from(module_id: ModuleId) -> Self { + (module_id.address, module_id.name) + } +} + +impl ModuleId { + pub fn new(address: AccountAddress, name: Identifier) -> Self { + ModuleId { address, name } + } + + pub fn name(&self) -> &IdentStr { + &self.name + } + + pub fn address(&self) -> &AccountAddress { + &self.address + } + + pub fn access_vector(&self) -> Vec { + let mut key = vec![CODE_TAG]; + key.append(&mut bcs::to_bytes(self).unwrap()); + key + } + + pub fn to_canonical_string(&self, with_prefix: bool) -> String { + self.to_canonical_display(with_prefix).to_string() + } + + /// Proxy type for overriding `ModuleId`'s display implementation, to use a + /// canonical form (full-width addresses), with an optional "0x" prefix + /// (controlled by the `with_prefix` flag). + pub fn to_canonical_display(&self, with_prefix: bool) -> impl Display + '_ { + struct IdDisplay<'a> { + id: &'a ModuleId, + with_prefix: bool, + } + + impl<'a> Display for IdDisplay<'a> { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!( + f, + "{}::{}", + self.id.address.to_canonical_display(self.with_prefix), + self.id.name, + ) + } + } + + IdDisplay { + id: self, + with_prefix, + } + } +} + +impl Display for ModuleId { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self.to_canonical_display(/* with_prefix */ false)) + } +} + +impl ModuleId { + pub fn short_str_lossless(&self) -> String { + format!("0x{}::{}", self.address.short_str_lossless(), self.name) + } +} + +impl Display for StructTag { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!( + f, + "0x{}::{}::{}", + self.address.short_str_lossless(), + self.module, + self.name + )?; + if let Some(first_ty) = self.type_params.first() { + write!(f, "<")?; + write!(f, "{}", first_ty)?; + for ty in self.type_params.iter().skip(1) { + write!(f, ", {}", ty)?; + } + write!(f, ">")?; + } + Ok(()) + } +} + +impl Display for TypeTag { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + match self { + TypeTag::Struct(s) => write!(f, "{}", s), + TypeTag::Vector(ty) => write!(f, "vector<{}>", ty), + TypeTag::U8 => write!(f, "u8"), + TypeTag::U16 => write!(f, "u16"), + TypeTag::U32 => write!(f, "u32"), + TypeTag::U64 => write!(f, "u64"), + TypeTag::U128 => write!(f, "u128"), + TypeTag::U256 => write!(f, "u256"), + TypeTag::Address => write!(f, "address"), + TypeTag::Signer => write!(f, "signer"), + TypeTag::Bool => write!(f, "bool"), + } + } +} + +impl Display for ResourceKey { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "0x{}/{}", self.address.short_str_lossless(), self.type_) + } +} + +impl From for TypeTag { + fn from(t: StructTag) -> TypeTag { + TypeTag::Struct(Box::new(t)) + } +} + +#[cfg(test)] +mod tests { + use std::mem; + + use super::{ModuleId, TypeTag}; + use crate::{ + account_address::AccountAddress, ident_str, identifier::Identifier, + language_storage::StructTag, + }; + + #[test] + fn test_type_tag_serde() { + let a = TypeTag::Struct(Box::new(StructTag { + address: AccountAddress::ONE, + module: Identifier::from_utf8(("abc".as_bytes()).to_vec()).unwrap(), + name: Identifier::from_utf8(("abc".as_bytes()).to_vec()).unwrap(), + type_params: vec![TypeTag::U8], + })); + let b = serde_json::to_string(&a).unwrap(); + let c: TypeTag = serde_json::from_str(&b).unwrap(); + assert!(a.eq(&c), "Typetag serde error"); + assert_eq!(mem::size_of::(), 16); + } + + #[test] + fn test_module_id_display() { + let id = ModuleId::new(AccountAddress::ONE, ident_str!("foo").to_owned()); + + assert_eq!( + format!("{id}"), + "0000000000000000000000000000000000000000000000000000000000000001::foo", + ); + + assert_eq!( + format!("{}", id.to_canonical_display(/* with_prefix */ false)), + "0000000000000000000000000000000000000000000000000000000000000001::foo", + ); + + assert_eq!( + format!("{}", id.to_canonical_display(/* with_prefix */ true)), + "0x0000000000000000000000000000000000000000000000000000000000000001::foo", + ); + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/mod.rs b/identity_iota_interaction/src/sdk_types/move_core_types/mod.rs new file mode 100644 index 0000000000..5426a426b8 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/mod.rs @@ -0,0 +1,33 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub mod account_address; +pub mod annotated_value; +pub mod annotated_visitor; +pub mod identifier; +pub mod language_storage; +pub mod parser; +pub mod runtime_value; +pub mod u256; + +use std::fmt; + +pub(crate) fn fmt_list( + f: &mut fmt::Formatter<'_>, + begin: &str, + items: impl IntoIterator, + end: &str, +) -> fmt::Result { + write!(f, "{}", begin)?; + let mut items = items.into_iter(); + if let Some(x) = items.next() { + write!(f, "{}", x)?; + for x in items { + write!(f, ", {}", x)?; + } + } + write!(f, "{}", end)?; + Ok(()) +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parser.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parser.rs new file mode 100644 index 0000000000..949533cae6 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/parser.rs @@ -0,0 +1,358 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::iter::Peekable; + +use anyhow::{bail, format_err, Result}; + +use super::super::move_types::identifier; +use super::super::move_types::account_address::AccountAddress; +use super::super::move_types::identifier::Identifier; + +use super::language_storage::{TypeTag, StructTag}; + +#[derive(Eq, PartialEq, Debug)] +enum Token { + U8Type, + U16Type, + U32Type, + U64Type, + U128Type, + U256Type, + BoolType, + AddressType, + VectorType, + SignerType, + Whitespace(String), + Name(String), + Address(String), + U8(String), + U16(String), + U32(String), + U64(String), + U128(String), + U256(String), + + Bytes(String), + True, + False, + ColonColon, + Lt, + Gt, + Comma, + EOF, +} + +impl Token { + fn is_whitespace(&self) -> bool { + matches!(self, Self::Whitespace(_)) + } +} + +fn token_as_name(tok: Token) -> Result { + use Token::*; + Ok(match tok { + U8Type => "u8".to_string(), + U16Type => "u16".to_string(), + U32Type => "u32".to_string(), + U64Type => "u64".to_string(), + U128Type => "u128".to_string(), + U256Type => "u256".to_string(), + BoolType => "bool".to_string(), + AddressType => "address".to_string(), + VectorType => "vector".to_string(), + True => "true".to_string(), + False => "false".to_string(), + SignerType => "signer".to_string(), + Name(s) => s, + Whitespace(_) | Address(_) | U8(_) | U16(_) | U32(_) | U64(_) | U128(_) | U256(_) + | Bytes(_) | ColonColon | Lt | Gt | Comma | EOF => { + bail!("Invalid token. Expected a name but got {:?}", tok) + } + }) +} + +fn name_token(s: String) -> Token { + match s.as_str() { + "u8" => Token::U8Type, + "u16" => Token::U16Type, + "u32" => Token::U32Type, + "u64" => Token::U64Type, + "u128" => Token::U128Type, + "u256" => Token::U256Type, + "bool" => Token::BoolType, + "address" => Token::AddressType, + "vector" => Token::VectorType, + "true" => Token::True, + "false" => Token::False, + "signer" => Token::SignerType, + _ => Token::Name(s), + } +} + +fn next_number(initial: char, mut it: impl Iterator) -> Result<(Token, usize)> { + let mut num = String::new(); + num.push(initial); + loop { + match it.next() { + Some(c) if c.is_ascii_digit() || c == '_' => num.push(c), + Some(c) if c.is_alphanumeric() => { + let mut suffix = String::new(); + suffix.push(c); + loop { + match it.next() { + Some(c) if c.is_ascii_alphanumeric() => suffix.push(c), + _ => { + let len = num.len() + suffix.len(); + let tok = match suffix.as_str() { + "u8" => Token::U8(num), + "u16" => Token::U16(num), + "u32" => Token::U32(num), + "u64" => Token::U64(num), + "u128" => Token::U128(num), + "u256" => Token::U256(num), + _ => bail!("invalid suffix"), + }; + return Ok((tok, len)); + } + } + } + } + _ => { + let len = num.len(); + return Ok((Token::U64(num), len)); + } + } + } +} + +#[allow(clippy::many_single_char_names)] +fn next_token(s: &str) -> Result> { + let mut it = s.chars().peekable(); + match it.next() { + None => Ok(None), + Some(c) => Ok(Some(match c { + '<' => (Token::Lt, 1), + '>' => (Token::Gt, 1), + ',' => (Token::Comma, 1), + ':' => match it.next() { + Some(':') => (Token::ColonColon, 2), + _ => bail!("unrecognized token"), + }, + '0' if it.peek() == Some(&'x') || it.peek() == Some(&'X') => { + it.next().unwrap(); + match it.next() { + Some(c) if c.is_ascii_hexdigit() => { + let mut r = String::new(); + r.push('0'); + r.push('x'); + r.push(c); + for c in it { + if c.is_ascii_hexdigit() { + r.push(c); + } else { + break; + } + } + let len = r.len(); + (Token::Address(r), len) + } + _ => bail!("unrecognized token"), + } + } + c if c.is_ascii_digit() => next_number(c, it)?, + 'b' if it.peek() == Some(&'"') => { + it.next().unwrap(); + let mut r = String::new(); + loop { + match it.next() { + Some('"') => break, + Some(c) if c.is_ascii() => r.push(c), + _ => bail!("unrecognized token"), + } + } + let len = r.len() + 3; + (Token::Bytes(hex::encode(r)), len) + } + 'x' if it.peek() == Some(&'"') => { + it.next().unwrap(); + let mut r = String::new(); + loop { + match it.next() { + Some('"') => break, + Some(c) if c.is_ascii_hexdigit() => r.push(c), + _ => bail!("unrecognized token"), + } + } + let len = r.len() + 3; + (Token::Bytes(r), len) + } + c if c.is_ascii_whitespace() => { + let mut r = String::new(); + r.push(c); + for c in it { + if c.is_ascii_whitespace() { + r.push(c); + } else { + break; + } + } + let len = r.len(); + (Token::Whitespace(r), len) + } + c if c.is_ascii_alphabetic() => { + let mut r = String::new(); + r.push(c); + for c in it { + if identifier::is_valid_identifier_char(c) { + r.push(c); + } else { + break; + } + } + let len = r.len(); + (name_token(r), len) + } + _ => bail!("unrecognized token"), + })), + } +} + +fn tokenize(mut s: &str) -> Result> { + let mut v = vec![]; + while let Some((tok, n)) = next_token(s)? { + v.push(tok); + s = &s[n..]; + } + Ok(v) +} + +struct Parser> { + it: Peekable, +} + +impl> Parser { + fn new>(v: T) -> Self { + Self { + it: v.into_iter().peekable(), + } + } + + fn next(&mut self) -> Result { + match self.it.next() { + Some(tok) => Ok(tok), + None => bail!("out of tokens, this should not happen"), + } + } + + fn peek(&mut self) -> Option<&Token> { + self.it.peek() + } + + fn consume(&mut self, tok: Token) -> Result<()> { + let t = self.next()?; + if t != tok { + bail!("expected token {:?}, got {:?}", tok, t) + } + Ok(()) + } + + fn parse_comma_list( + &mut self, + parse_list_item: F, + end_token: Token, + allow_trailing_comma: bool, + ) -> Result> + where + F: Fn(&mut Self) -> Result, + R: std::fmt::Debug, + { + let mut v = vec![]; + if !(self.peek() == Some(&end_token)) { + loop { + v.push(parse_list_item(self)?); + if self.peek() == Some(&end_token) { + break; + } + self.consume(Token::Comma)?; + if self.peek() == Some(&end_token) && allow_trailing_comma { + break; + } + } + } + Ok(v) + } + + fn parse_type_tag(&mut self) -> Result { + Ok(match self.next()? { + Token::U8Type => TypeTag::U8, + Token::U16Type => TypeTag::U16, + Token::U32Type => TypeTag::U32, + Token::U64Type => TypeTag::U64, + Token::U128Type => TypeTag::U128, + Token::U256Type => TypeTag::U256, + Token::BoolType => TypeTag::Bool, + Token::AddressType => TypeTag::Address, + Token::SignerType => TypeTag::Signer, + Token::VectorType => { + self.consume(Token::Lt)?; + let ty = self.parse_type_tag()?; + self.consume(Token::Gt)?; + TypeTag::Vector(Box::new(ty)) + } + Token::Address(addr) => { + self.consume(Token::ColonColon)?; + let module = self.next().and_then(token_as_name)?; + self.consume(Token::ColonColon)?; + let name = self.next().and_then(token_as_name)?; + let ty_args = if self.peek() == Some(&Token::Lt) { + self.next()?; + let ty_args = + self.parse_comma_list(|parser| parser.parse_type_tag(), Token::Gt, true)?; + self.consume(Token::Gt)?; + ty_args + } else { + vec![] + }; + TypeTag::Struct(Box::new(StructTag { + address: AccountAddress::from_hex_literal(&addr)?, + module: Identifier::new(module)?, + name: Identifier::new(name)?, + type_params: ty_args, + })) + } + tok => bail!("unexpected token {:?}, expected type tag", tok), + }) + } +} + +fn parse(s: &str, f: F) -> Result + where + F: Fn(&mut Parser>) -> Result, +{ + let mut tokens: Vec<_> = tokenize(s)? + .into_iter() + .filter(|tok| !tok.is_whitespace()) + .collect(); + tokens.push(Token::EOF); + let mut parser = Parser::new(tokens); + let res = f(&mut parser)?; + parser.consume(Token::EOF)?; + Ok(res) +} + +pub fn parse_type_tag(s: &str) -> Result { + parse(s, |parser| parser.parse_type_tag()) +} + +pub fn parse_struct_tag(s: &str) -> Result { + let type_tag = parse(s, |parser| parser.parse_type_tag()) + .map_err(|e| format_err!("invalid struct tag: {}, {}", s, e))?; + if let TypeTag::Struct(struct_tag) = type_tag { + Ok(*struct_tag) + } else { + bail!("invalid struct tag: {}", s) + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs b/identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs new file mode 100644 index 0000000000..3ecaf59e83 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs @@ -0,0 +1,397 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::{self, Debug}; + +use serde::{ + de::Error as DeError, + ser::{SerializeSeq, SerializeTuple}, + Deserialize, Serialize, +}; + +use anyhow::{anyhow, Result as AResult}; +//use move_proc_macros::test_variant_order; +// use crate::{ +// de::Error as DeError, +// ser::{SerializeSeq, SerializeTuple}, +// Deserialize, Serialize, +// }; + +use super::{account_address::AccountAddress, annotated_value as A, fmt_list, u256}; + +/// In the `WithTypes` configuration, a Move struct gets serialized into a Serde +/// struct with this name +pub const MOVE_STRUCT_NAME: &str = "struct"; + +/// In the `WithTypes` configuration, a Move struct gets serialized into a Serde +/// struct with this as the first field +pub const MOVE_STRUCT_TYPE: &str = "type"; + +/// In the `WithTypes` configuration, a Move struct gets serialized into a Serde +/// struct with this as the second field +pub const MOVE_STRUCT_FIELDS: &str = "fields"; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct MoveStruct(pub Vec); + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum MoveValue { + U8(u8), + U64(u64), + U128(u128), + Bool(bool), + Address(AccountAddress), + Vector(Vec), + Struct(MoveStruct), + Signer(AccountAddress), + // NOTE: Added in bytecode version v6, do not reorder! + U16(u16), + U32(u32), + U256(u256::U256), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MoveStructLayout(pub Vec); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum MoveTypeLayout { + #[serde(rename(serialize = "bool", deserialize = "bool"))] + Bool, + #[serde(rename(serialize = "u8", deserialize = "u8"))] + U8, + #[serde(rename(serialize = "u64", deserialize = "u64"))] + U64, + #[serde(rename(serialize = "u128", deserialize = "u128"))] + U128, + #[serde(rename(serialize = "address", deserialize = "address"))] + Address, + #[serde(rename(serialize = "vector", deserialize = "vector"))] + Vector(Box), + #[serde(rename(serialize = "struct", deserialize = "struct"))] + Struct(MoveStructLayout), + #[serde(rename(serialize = "signer", deserialize = "signer"))] + Signer, + + // NOTE: Added in bytecode version v6, do not reorder! + #[serde(rename(serialize = "u16", deserialize = "u16"))] + U16, + #[serde(rename(serialize = "u32", deserialize = "u32"))] + U32, + #[serde(rename(serialize = "u256", deserialize = "u256"))] + U256, +} + +impl MoveValue { + pub fn simple_deserialize(blob: &[u8], ty: &MoveTypeLayout) -> AResult { + Ok(bcs::from_bytes_seed(ty, blob)?) + } + + pub fn simple_serialize(&self) -> Option> { + bcs::to_bytes(self).ok() + } + + pub fn vector_u8(v: Vec) -> Self { + MoveValue::Vector(v.into_iter().map(MoveValue::U8).collect()) + } + + /// Converts the `Vec` to a `Vec` if the inner `MoveValue` is + /// a `MoveValue::U8`, or returns an error otherwise. + pub fn vec_to_vec_u8(vec: Vec) -> AResult> { + let mut vec_u8 = Vec::with_capacity(vec.len()); + + for byte in vec { + match byte { + MoveValue::U8(u8) => { + vec_u8.push(u8); + } + _ => { + return Err(anyhow!( + "Expected inner MoveValue in Vec to be a MoveValue::U8" + .to_string(), + )); + } + } + } + Ok(vec_u8) + } + + pub fn vector_address(v: Vec) -> Self { + MoveValue::Vector(v.into_iter().map(MoveValue::Address).collect()) + } + + pub fn decorate(self, layout: &A::MoveTypeLayout) -> A::MoveValue { + match (self, layout) { + (MoveValue::Struct(s), A::MoveTypeLayout::Struct(l)) => { + A::MoveValue::Struct(s.decorate(l)) + } + (MoveValue::Vector(vals), A::MoveTypeLayout::Vector(t)) => { + A::MoveValue::Vector(vals.into_iter().map(|v| v.decorate(t)).collect()) + } + (MoveValue::U8(a), _) => A::MoveValue::U8(a), + (MoveValue::U64(u), _) => A::MoveValue::U64(u), + (MoveValue::U128(u), _) => A::MoveValue::U128(u), + (MoveValue::Bool(b), _) => A::MoveValue::Bool(b), + (MoveValue::Address(a), _) => A::MoveValue::Address(a), + (MoveValue::Signer(a), _) => A::MoveValue::Signer(a), + (MoveValue::U16(u), _) => A::MoveValue::U16(u), + (MoveValue::U32(u), _) => A::MoveValue::U32(u), + (MoveValue::U256(u), _) => A::MoveValue::U256(u), + _ => panic!("Invalid decoration"), + } + } +} + +pub fn serialize_values<'a, I>(vals: I) -> Vec> +where + I: IntoIterator, +{ + vals.into_iter() + .map(|val| { + val.simple_serialize() + .expect("serialization should succeed") + }) + .collect() +} + +impl MoveStruct { + pub fn new(value: Vec) -> Self { + Self(value) + } + + pub fn simple_deserialize(blob: &[u8], ty: &MoveStructLayout) -> AResult { + Ok(bcs::from_bytes_seed(ty, blob)?) + } + + pub fn decorate(self, layout: &A::MoveStructLayout) -> A::MoveStruct { + let MoveStruct(vals) = self; + let A::MoveStructLayout { type_, fields } = layout; + A::MoveStruct { + type_: type_.clone(), + fields: vals + .into_iter() + .zip(fields) + .map(|(v, l)| (l.name.clone(), v.decorate(&l.layout))) + .collect(), + } + } + + pub fn fields(&self) -> &[MoveValue] { + &self.0 + } + + pub fn into_fields(self) -> Vec { + self.0 + } +} + +impl MoveStructLayout { + pub fn new(types: Vec) -> Self { + Self(types) + } + + pub fn fields(&self) -> &[MoveTypeLayout] { + &self.0 + } + + pub fn into_fields(self) -> Vec { + self.0 + } +} + +impl<'d> serde::de::DeserializeSeed<'d> for &MoveTypeLayout { + type Value = MoveValue; + fn deserialize>( + self, + deserializer: D, + ) -> Result { + match self { + MoveTypeLayout::Bool => bool::deserialize(deserializer).map(MoveValue::Bool), + MoveTypeLayout::U8 => u8::deserialize(deserializer).map(MoveValue::U8), + MoveTypeLayout::U16 => u16::deserialize(deserializer).map(MoveValue::U16), + MoveTypeLayout::U32 => u32::deserialize(deserializer).map(MoveValue::U32), + MoveTypeLayout::U64 => u64::deserialize(deserializer).map(MoveValue::U64), + MoveTypeLayout::U128 => u128::deserialize(deserializer).map(MoveValue::U128), + MoveTypeLayout::U256 => u256::U256::deserialize(deserializer).map(MoveValue::U256), + MoveTypeLayout::Address => { + AccountAddress::deserialize(deserializer).map(MoveValue::Address) + } + MoveTypeLayout::Signer => { + AccountAddress::deserialize(deserializer).map(MoveValue::Signer) + } + MoveTypeLayout::Struct(ty) => Ok(MoveValue::Struct(ty.deserialize(deserializer)?)), + MoveTypeLayout::Vector(layout) => Ok(MoveValue::Vector( + deserializer.deserialize_seq(VectorElementVisitor(layout))?, + )), + } + } +} + +struct VectorElementVisitor<'a>(&'a MoveTypeLayout); + +impl<'d, 'a> serde::de::Visitor<'d> for VectorElementVisitor<'a> { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("Vector") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'d>, + { + let mut vals = Vec::new(); + while let Some(elem) = seq.next_element_seed(self.0)? { + vals.push(elem) + } + Ok(vals) + } +} + +struct StructFieldVisitor<'a>(&'a [MoveTypeLayout]); + +impl<'d, 'a> serde::de::Visitor<'d> for StructFieldVisitor<'a> { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("Struct") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'d>, + { + let mut val = Vec::new(); + for (i, field_type) in self.0.iter().enumerate() { + match seq.next_element_seed(field_type)? { + Some(elem) => val.push(elem), + None => return Err(A::Error::invalid_length(i, &self)), + } + } + Ok(val) + } +} + +impl<'d> serde::de::DeserializeSeed<'d> for &MoveStructLayout { + type Value = MoveStruct; + + fn deserialize>( + self, + deserializer: D, + ) -> Result { + Ok(MoveStruct(deserializer.deserialize_tuple( + self.0.len(), + StructFieldVisitor(&self.0), + )?)) + } +} + +impl serde::Serialize for MoveValue { + fn serialize(&self, serializer: S) -> Result { + match self { + MoveValue::Struct(s) => s.serialize(serializer), + MoveValue::Bool(b) => serializer.serialize_bool(*b), + MoveValue::U8(i) => serializer.serialize_u8(*i), + MoveValue::U16(i) => serializer.serialize_u16(*i), + MoveValue::U32(i) => serializer.serialize_u32(*i), + MoveValue::U64(i) => serializer.serialize_u64(*i), + MoveValue::U128(i) => serializer.serialize_u128(*i), + MoveValue::U256(i) => i.serialize(serializer), + MoveValue::Address(a) => a.serialize(serializer), + MoveValue::Signer(a) => a.serialize(serializer), + MoveValue::Vector(v) => { + let mut t = serializer.serialize_seq(Some(v.len()))?; + for val in v { + t.serialize_element(val)?; + } + t.end() + } + } + } +} + +impl serde::Serialize for MoveStruct { + fn serialize(&self, serializer: S) -> Result { + let mut t = serializer.serialize_tuple(self.0.len())?; + for v in self.0.iter() { + t.serialize_element(v)?; + } + t.end() + } +} + +impl fmt::Display for MoveTypeLayout { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + use MoveTypeLayout::*; + match self { + Bool => write!(f, "bool"), + U8 => write!(f, "u8"), + U16 => write!(f, "u16"), + U32 => write!(f, "u32"), + U64 => write!(f, "u64"), + U128 => write!(f, "u128"), + U256 => write!(f, "u256"), + Address => write!(f, "address"), + Signer => write!(f, "signer"), + Vector(typ) if f.alternate() => write!(f, "vector<{typ:#}>"), + Vector(typ) => write!(f, "vector<{typ}>"), + Struct(s) if f.alternate() => write!(f, "{s:#}"), + Struct(s) => write!(f, "{s}"), + } + } +} + +/// Helper type that uses `T`'s `Display` implementation as its own `Debug` +/// implementation, to allow other `Display` implementations in this module to +/// take advantage of the structured formatting helpers that Rust uses for its +/// own debug types. +struct DebugAsDisplay<'a, T>(&'a T); +impl<'a, T: fmt::Display> fmt::Debug for DebugAsDisplay<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "{:#}", self.0) + } else { + write!(f, "{}", self.0) + } + } +} + +impl fmt::Display for MoveStructLayout { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + use DebugAsDisplay as DD; + + write!(f, "struct ")?; + let mut map = f.debug_map(); + for (i, l) in self.0.iter().enumerate() { + map.entry(&i, &DD(&l)); + } + + map.finish() + } +} + +impl fmt::Display for MoveValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MoveValue::U8(u) => write!(f, "{}u8", u), + MoveValue::U16(u) => write!(f, "{}u16", u), + MoveValue::U32(u) => write!(f, "{}u32", u), + MoveValue::U64(u) => write!(f, "{}u64", u), + MoveValue::U128(u) => write!(f, "{}u128", u), + MoveValue::U256(u) => write!(f, "{}u256", u), + MoveValue::Bool(false) => write!(f, "false"), + MoveValue::Bool(true) => write!(f, "true"), + MoveValue::Address(a) => write!(f, "{}", a.to_hex_literal()), + MoveValue::Signer(a) => write!(f, "signer({})", a.to_hex_literal()), + MoveValue::Vector(v) => fmt_list(f, "vector[", v, "]"), + MoveValue::Struct(s) => fmt::Display::fmt(s, f), + } + } +} + +impl fmt::Display for MoveStruct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt_list(f, "struct[", &self.0, "]") + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/u256.rs b/identity_iota_interaction/src/sdk_types/move_core_types/u256.rs new file mode 100644 index 0000000000..51f91391d1 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/u256.rs @@ -0,0 +1,379 @@ +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + fmt, + mem::size_of, + ops::{ + Shl, Shr, + }, +}; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use uint::FromStrRadixErr; + +// This U256 impl was chosen for now but we are open to changing it as needed +use primitive_types::U256 as PrimitiveU256; + +const NUM_BITS_PER_BYTE: usize = 8; +const U256_NUM_BITS: usize = 256; +pub const U256_NUM_BYTES: usize = U256_NUM_BITS / NUM_BITS_PER_BYTE; + +#[derive(Debug)] +pub struct U256FromStrError(FromStrRadixErr); + +/// A list of error categories encountered when parsing numbers. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub enum U256CastErrorKind { + /// Value too large to fit in U8. + TooLargeForU8, + + /// Value too large to fit in U16. + TooLargeForU16, + + /// Value too large to fit in U32. + TooLargeForU32, + + /// Value too large to fit in U64. + TooLargeForU64, + + /// Value too large to fit in U128. + TooLargeForU128, +} + +#[derive(Debug)] +pub struct U256CastError { + kind: U256CastErrorKind, + val: U256, +} + +impl U256CastError { + pub fn new>(val: T, kind: U256CastErrorKind) -> Self { + Self { + kind, + val: val.into(), + } + } +} + +impl std::error::Error for U256CastError {} + +impl fmt::Display for U256CastError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let type_str = match self.kind { + U256CastErrorKind::TooLargeForU8 => "u8", + U256CastErrorKind::TooLargeForU16 => "u16", + U256CastErrorKind::TooLargeForU32 => "u32", + U256CastErrorKind::TooLargeForU64 => "u64", + U256CastErrorKind::TooLargeForU128 => "u128", + }; + let err_str = format!("Cast failed. {} too large for {}.", self.val, type_str); + write!(f, "{err_str}") + } +} + +impl std::error::Error for U256FromStrError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } +} + +impl fmt::Display for U256FromStrError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy, PartialOrd, Ord, Default)] +pub struct U256(PrimitiveU256); + +impl fmt::Display for U256 { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::UpperHex for U256 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl fmt::LowerHex for U256 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl std::str::FromStr for U256 { + type Err = U256FromStrError; + + fn from_str(s: &str) -> Result { + Self::from_str_radix(s, 10) + } +} + +impl<'de> Deserialize<'de> for U256 { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + Ok(U256::from_le_bytes( + &(<[u8; U256_NUM_BYTES]>::deserialize(deserializer)?), + )) + } +} + +impl Serialize for U256 { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + self.to_le_bytes().serialize(serializer) + } +} + +impl U256 { + /// Zero value as U256 + pub const fn zero() -> Self { + Self(PrimitiveU256::zero()) + } + + /// One value as U256 + pub const fn one() -> Self { + Self(PrimitiveU256::one()) + } + + /// Max value of U256: + /// 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + pub const fn max_value() -> Self { + Self(PrimitiveU256::max_value()) + } + + /// U256 from string with radix 10 or 16 + pub fn from_str_radix(src: &str, radix: u32) -> Result { + PrimitiveU256::from_str_radix(src.trim_start_matches('0'), radix) + .map(Self) + .map_err(U256FromStrError) + } + + /// U256 from 32 little endian bytes + pub fn from_le_bytes(slice: &[u8; U256_NUM_BYTES]) -> Self { + Self(PrimitiveU256::from_little_endian(slice)) + } + + /// U256 to 32 little endian bytes + pub fn to_le_bytes(self) -> [u8; U256_NUM_BYTES] { + let mut bytes = [0u8; U256_NUM_BYTES]; + self.0.to_little_endian(&mut bytes); + bytes + } + + /// Leading zeros of the number + pub fn leading_zeros(&self) -> u32 { + self.0.leading_zeros() + } + + // Unchecked downcasting. Values as truncated if larger than target max + pub fn unchecked_as_u8(&self) -> u8 { + self.0.low_u128() as u8 + } + + pub fn unchecked_as_u16(&self) -> u16 { + self.0.low_u128() as u16 + } + + pub fn unchecked_as_u32(&self) -> u32 { + self.0.low_u128() as u32 + } + + pub fn unchecked_as_u64(&self) -> u64 { + self.0.low_u128() as u64 + } + + pub fn unchecked_as_u128(&self) -> u128 { + self.0.low_u128() + } + + // Check arithmetic + /// Checked integer addition. Computes self + rhs, returning None if + /// overflow occurred. + pub fn checked_add(self, rhs: Self) -> Option { + self.0.checked_add(rhs.0).map(Self) + } + + /// Checked integer subtraction. Computes self - rhs, returning None if + /// overflow occurred. + pub fn checked_sub(self, rhs: Self) -> Option { + self.0.checked_sub(rhs.0).map(Self) + } + + /// Checked integer multiplication. Computes self * rhs, returning None if + /// overflow occurred. + pub fn checked_mul(self, rhs: Self) -> Option { + self.0.checked_mul(rhs.0).map(Self) + } + + /// Checked integer division. Computes self / rhs, returning None if rhs == + /// 0. + pub fn checked_div(self, rhs: Self) -> Option { + self.0.checked_div(rhs.0).map(Self) + } + + /// Checked integer remainder. Computes self % rhs, returning None if rhs == + /// 0. + pub fn checked_rem(self, rhs: Self) -> Option { + self.0.checked_rem(rhs.0).map(Self) + } + + /// Checked integer remainder. Computes self % rhs, returning None if rhs == + /// 0. + pub fn checked_shl(self, rhs: u32) -> Option { + if rhs >= U256_NUM_BITS as u32 { + return None; + } + Some(Self(self.0.shl(rhs))) + } + + /// Checked shift right. Computes self >> rhs, returning None if rhs is + /// larger than or equal to the number of bits in self. + pub fn checked_shr(self, rhs: u32) -> Option { + if rhs >= U256_NUM_BITS as u32 { + return None; + } + Some(Self(self.0.shr(rhs))) + } + + /// Downcast to a an unsigned value of type T + /// T must be at most u128 + pub fn down_cast_lossy>(self) -> T { + // Size of this type + let type_size = size_of::(); + // Maximum value for this type + let max_val: u128 = if type_size < 16 { + (1u128 << (NUM_BITS_PER_BYTE * type_size)) - 1u128 + } else { + u128::MAX + }; + // This should never fail + match T::try_from(self.0.low_u128() & max_val) { + Ok(w) => w, + Err(_) => panic!("Fatal! Downcast failed"), + } + } + + /// Wrapping integer addition. Computes self + rhs, wrapping around at the + /// boundary of the type. By definition in std::instrinsics, + /// a.wrapping_add(b) = (a + b) % (2^N), where N is bit width + pub fn wrapping_add(self, rhs: Self) -> Self { + Self(self.0.overflowing_add(rhs.0).0) + } + + /// Wrapping integer subtraction. Computes self - rhs, wrapping around at + /// the boundary of the type. By definition in std::instrinsics, + /// a.wrapping_add(b) = (a - b) % (2^N), where N is bit width + pub fn wrapping_sub(self, rhs: Self) -> Self { + Self(self.0.overflowing_sub(rhs.0).0) + } + + /// Wrapping integer multiplication. Computes self * rhs, wrapping around + /// at the boundary of the type. By definition in std::instrinsics, + /// a.wrapping_mul(b) = (a * b) % (2^N), where N is bit width + pub fn wrapping_mul(self, rhs: Self) -> Self { + Self(self.0.overflowing_mul(rhs.0).0) + } +} + +impl From for U256 { + fn from(n: u8) -> Self { + U256(PrimitiveU256::from(n)) + } +} + +impl From for U256 { + fn from(n: u16) -> Self { + U256(PrimitiveU256::from(n)) + } +} + +impl From for U256 { + fn from(n: u32) -> Self { + U256(PrimitiveU256::from(n)) + } +} + +impl From for U256 { + fn from(n: u64) -> Self { + U256(PrimitiveU256::from(n)) + } +} + +impl From for U256 { + fn from(n: u128) -> Self { + U256(PrimitiveU256::from(n)) + } +} + +impl TryFrom for u8 { + type Error = U256CastError; + fn try_from(n: U256) -> Result { + let n = n.0.low_u64(); + if n > u8::MAX as u64 { + Err(U256CastError::new(n, U256CastErrorKind::TooLargeForU8)) + } else { + Ok(n as u8) + } + } +} + +impl TryFrom for u16 { + type Error = U256CastError; + + fn try_from(n: U256) -> Result { + let n = n.0.low_u64(); + if n > u16::MAX as u64 { + Err(U256CastError::new(n, U256CastErrorKind::TooLargeForU16)) + } else { + Ok(n as u16) + } + } +} + +impl TryFrom for u32 { + type Error = U256CastError; + + fn try_from(n: U256) -> Result { + let n = n.0.low_u64(); + if n > u32::MAX as u64 { + Err(U256CastError::new(n, U256CastErrorKind::TooLargeForU32)) + } else { + Ok(n as u32) + } + } +} + +impl TryFrom for u64 { + type Error = U256CastError; + + fn try_from(n: U256) -> Result { + let n = n.0.low_u128(); + if n > u64::MAX as u128 { + Err(U256CastError::new(n, U256CastErrorKind::TooLargeForU64)) + } else { + Ok(n as u64) + } + } +} + +impl TryFrom for u128 { + type Error = U256CastError; + + fn try_from(n: U256) -> Result { + if n > U256::from(u128::MAX) { + Err(U256CastError::new(n, U256CastErrorKind::TooLargeForU128)) + } else { + Ok(n.0.low_u128()) + } + } +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/shared_crypto/intent.rs b/identity_iota_interaction/src/sdk_types/shared_crypto/intent.rs new file mode 100644 index 0000000000..88cec46952 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/shared_crypto/intent.rs @@ -0,0 +1,204 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; +use std::vec::Vec; +use std::default::Default; +use std::convert::{TryFrom, TryInto}; +use std::result::Result::{Ok, Err}; + +use eyre::eyre; +use fastcrypto::encoding::decode_bytes_hex; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +use Result; + +pub const INTENT_PREFIX_LENGTH: usize = 3; + +/// The version here is to distinguish between signing different versions of the +/// struct or enum. Serialized output between two different versions of the same +/// struct/enum might accidentally (or maliciously on purpose) match. +#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[repr(u8)] +pub enum IntentVersion { + V0 = 0, +} + +impl TryFrom for IntentVersion { + type Error = eyre::Report; + fn try_from(value: u8) -> Result { + bcs::from_bytes(&[value]).map_err(|_| eyre!("Invalid IntentVersion")) + } +} + +/// This enums specifies the application ID. Two intents in two different +/// applications (i.e., Narwhal, Iota, Ethereum etc) should never collide, so +/// that even when a signing key is reused, nobody can take a signature +/// designated for app_1 and present it as a valid signature for an (any) intent +/// in app_2. +#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[repr(u8)] +pub enum AppId { + Iota = 0, + Narwhal = 1, + Consensus = 2, +} + +// TODO(joyqvq): Use num_derive +impl TryFrom for AppId { + type Error = eyre::Report; + fn try_from(value: u8) -> Result { + bcs::from_bytes(&[value]).map_err(|_| eyre!("Invalid AppId")) + } +} + +impl Default for AppId { + fn default() -> Self { + Self::Iota + } +} + +/// This enums specifies the intent scope. Two intents for different scope +/// should never collide, so no signature provided for one intent scope can be +/// used for another, even when the serialized data itself may be the same. +#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[repr(u8)] +pub enum IntentScope { + TransactionData = 0, // Used for a user signature on a transaction data. + TransactionEffects = 1, // Used for an authority signature on transaction effects. + CheckpointSummary = 2, // Used for an authority signature on a checkpoint summary. + PersonalMessage = 3, // Used for a user signature on a personal message. + SenderSignedTransaction = 4, // Used for an authority signature on a user signed transaction. + ProofOfPossession = 5, /* Used as a signature representing an authority's proof of + * possession of its authority protocol key. */ + HeaderDigest = 6, // Used for narwhal authority signature on header digest. + BridgeEventUnused = 7, // for bridge purposes but it's currently not included in messages. + ConsensusBlock = 8, // Used for consensus authority signature on block's digest +} + +impl TryFrom for IntentScope { + type Error = eyre::Report; + fn try_from(value: u8) -> Result { + bcs::from_bytes(&[value]).map_err(|_| eyre!("Invalid IntentScope")) + } +} + +/// An intent is a compact struct serves as the domain separator for a message +/// that a signature commits to. It consists of three parts: [enum IntentScope] +/// (what the type of the message is), [enum IntentVersion], [enum AppId] (what +/// application that the signature refers to). It is used to construct [struct +/// IntentMessage] that what a signature commits to. +/// +/// The serialization of an Intent is a 3-byte array where each field is +/// represented by a byte. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Hash)] +pub struct Intent { + pub scope: IntentScope, + pub version: IntentVersion, + pub app_id: AppId, +} + +impl FromStr for Intent { + type Err = eyre::Report; + fn from_str(s: &str) -> Result { + let s: Vec = decode_bytes_hex(s).map_err(|_| eyre!("Invalid Intent"))?; + if s.len() != 3 { + return Err(eyre!("Invalid Intent")); + } + Ok(Self { + scope: s[0].try_into()?, + version: s[1].try_into()?, + app_id: s[2].try_into()?, + }) + } +} + +impl Intent { + pub fn iota_app(scope: IntentScope) -> Self { + Self { + version: IntentVersion::V0, + scope, + app_id: AppId::Iota, + } + } + + pub fn iota_transaction() -> Self { + Self { + scope: IntentScope::TransactionData, + version: IntentVersion::V0, + app_id: AppId::Iota, + } + } + + pub fn personal_message() -> Self { + Self { + scope: IntentScope::PersonalMessage, + version: IntentVersion::V0, + app_id: AppId::Iota, + } + } + + pub fn narwhal_app(scope: IntentScope) -> Self { + Self { + scope, + version: IntentVersion::V0, + app_id: AppId::Narwhal, + } + } + + pub fn consensus_app(scope: IntentScope) -> Self { + Self { + scope, + version: IntentVersion::V0, + app_id: AppId::Consensus, + } + } +} + +/// Intent Message is a wrapper around a message with its intent. The message +/// can be any type that implements [trait Serialize]. *ALL* signatures in Iota +/// must commits to the intent message, not the message itself. This guarantees +/// any intent message signed in the system cannot collide with another since +/// they are domain separated by intent. +/// +/// The serialization of an IntentMessage is compact: it only appends three +/// bytes to the message itself. +#[derive(Debug, PartialEq, Eq, Serialize, Clone, Hash, Deserialize)] +pub struct IntentMessage { + pub intent: Intent, + pub value: T, +} + +impl IntentMessage { + pub fn new(intent: Intent, value: T) -> Self { + Self { intent, value } + } +} + +/// A person message that wraps around a byte array. +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct PersonalMessage { + pub message: Vec, +} + +pub trait SecureIntent: Serialize + private::SealedIntent {} + +pub(crate) mod private { + use super::IntentMessage; + + pub trait SealedIntent {} + impl SealedIntent for IntentMessage {} +} + +/// A 1-byte domain separator for hashing Object ID in Iota. It is starting from +/// 0xf0 to ensure no hashing collision for any ObjectID vs IotaAddress which is +/// derived as the hash of `flag || pubkey`. See +/// `iota_types::crypto::SignatureScheme::flag()`. +#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[repr(u8)] +pub enum HashingIntentScope { + ChildObjectId = 0xf0, + RegularObjectId = 0xf1, +} \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs b/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs new file mode 100644 index 0000000000..db49c84edf --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub mod intent; \ No newline at end of file diff --git a/identity_iota_interaction/src/transaction_builder_trait.rs b/identity_iota_interaction/src/transaction_builder_trait.rs new file mode 100644 index 0000000000..e6bdcc9f2e --- /dev/null +++ b/identity_iota_interaction/src/transaction_builder_trait.rs @@ -0,0 +1,15 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::ProgrammableTransactionBcs; + +pub trait TransactionBuilderT { + type Error; + type NativeTxBuilder; + + fn finish(self) -> Result; + + fn as_native_tx_builder(&mut self) -> &mut Self::NativeTxBuilder; + + fn into_native_tx_builder(self) -> Self::NativeTxBuilder; +} diff --git a/identity_resolver/Cargo.toml b/identity_resolver/Cargo.toml index ea00a9d6bd..813c510508 100644 --- a/identity_resolver/Cargo.toml +++ b/identity_resolver/Cargo.toml @@ -27,17 +27,18 @@ thiserror = { version = "1.0", default-features = false } version = "=1.4.0" path = "../identity_iota_core" default-features = false -features = ["send-sync-client-ext", "iota-client"] +features = ["iota-client"] optional = true [dev-dependencies] tokio = { version = "1.29.0", default-features = false, features = ["rt-multi-thread", "macros"] } [features] -default = ["revocation-bitmap", "iota"] +default = ["revocation-bitmap", "iota", "send-sync-client"] revocation-bitmap = ["identity_credential/revocation-bitmap", "identity_iota_core?/revocation-bitmap"] # Enables the IOTA integration for the resolver. -iota = ["dep:identity_iota_core", "identity_iota_core/iota-client"] +iota = ["dep:identity_iota_core"] +send-sync-client = ["identity_iota_core?/send-sync-client-ext"] [lints] workspace = true diff --git a/identity_storage/Cargo.toml b/identity_storage/Cargo.toml index e84a21c1f1..29ddb2ae99 100644 --- a/identity_storage/Cargo.toml +++ b/identity_storage/Cargo.toml @@ -21,12 +21,13 @@ identity_credential = { version = "=1.4.0", path = "../identity_credential", def identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } identity_iota_core = { version = "=1.4.0", path = "../identity_iota_core", default-features = false, optional = true } +identity_iota_interaction = { version = "=1.4.0", path = "../identity_iota_interaction", default-features = false, optional = true } identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } iota-crypto = { version = "0.23.2", default-features = false, features = ["ed25519", "random"], optional = true } json-proof-token = { workspace = true, optional = true } rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"], optional = true } seahash = { version = "4.1.0", default-features = false } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag = "v0.1.0", optional = true } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.1.0", optional = true } serde.workspace = true serde_json.workspace = true thiserror.workspace = true @@ -44,11 +45,11 @@ default = ["iota-document", "memstore"] # Exposes in-memory implementations of the storage traits intended exclusively for testing. memstore = ["dep:tokio", "dep:rand", "dep:iota-crypto"] # Enables `Send` + `Sync` bounds for the storage traits. -send-sync-storage = ["identity_iota_core?/send-sync-client-ext"] +send-sync-storage = ["identity_iota_core?/send-sync-client-ext", "secret-storage?/send-sync-storage"] # Implements the JwkStorageDocumentExt trait for IotaDocument iota-document = ["dep:identity_iota_core"] # enables support to sign via storage -storage-signer = ["identity_iota_core?/iota-client", "dep:secret-storage"] +storage-signer = ["identity_iota_core?/iota-client", "dep:secret-storage", "dep:identity_iota_interaction"] # Enables JSON Proof Token & BBS+ related features jpt-bbs-plus = [ "identity_credential/jpt-bbs-plus", diff --git a/identity_storage/src/storage/storage_signer.rs b/identity_storage/src/storage/storage_signer.rs index 1c64ba3c05..ff76fa8c67 100644 --- a/identity_storage/src/storage/storage_signer.rs +++ b/identity_storage/src/storage/storage_signer.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use identity_iota_core::rebased::client::IotaKeySignature; +use identity_iota_interaction::IotaKeySignature; use identity_verification::jwk::Jwk; use identity_verification::jwk::JwkParams; use identity_verification::jwu; diff --git a/rustfmt.toml b/rustfmt.toml index c0842c2114..103ed48b33 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -6,3 +6,6 @@ normalize_doc_attributes = false tab_spaces = 2 wrap_comments = true imports_granularity = "Item" +ignore = [ + "identity_iota_interaction/src/sdk_types", +]